From 222e802504d90ee6db8969f6a3405db194146eff Mon Sep 17 00:00:00 2001 From: diamante0018 Date: Wed, 21 May 2025 16:23:17 +0200 Subject: [PATCH] init --- LICENSE | 121 + raw/character/mp_character_cloak_test.gsc | 17 + raw/character/mp_character_sentinel.gsc | 16 + raw/codescripts/character.gsc | 281 + raw/codescripts/delete.gsc | 7 + raw/codescripts/struct.gsc | 11 + raw/common_scripts/_artcommon.gsc | 547 + raw/common_scripts/_bcs_location_trigs.gsc | 3782 +++ raw/common_scripts/_createfx.gsc | 3166 +++ raw/common_scripts/_createfxmenu.gsc | 968 + raw/common_scripts/_destructible.gsc | 5392 ++++ raw/common_scripts/_dynamic_world.gsc | 1793 ++ raw/common_scripts/_elevator.gsc | 1286 + raw/common_scripts/_exploder.gsc | 1122 + raw/common_scripts/_fx.gsc | 970 + raw/common_scripts/utility.gsc | 5660 ++++ .../berlin_hotel_lights_wall2.gsc | 19 + .../container_plastic_72x56x48_01_destp.gsc | 24 + raw/destructible_scripts/toy_chicken.gsc | 8 + raw/destructible_scripts/toy_electricbox4.gsc | 17 + .../toy_locker_double.gsc | 44 + raw/dev_code_post_gfx_mp | 0 raw/dev_code_pre_gfx_mp | 0 raw/dev_common_bots_mp | 0 raw/dev_common_core_mp | 0 raw/dev_common_mp | 3 + raw/dev_development_mp | 0 raw/dev_mp_comeback | 1 + raw/dev_mp_dam | 1 + raw/dev_mp_detroit | 0 raw/dev_mp_greenband | 0 raw/dev_mp_instinct | 0 raw/dev_mp_lab2 | 0 raw/dev_mp_laser2 | 0 raw/dev_mp_levity | 0 raw/dev_mp_prison | 0 raw/dev_mp_prison_z | 0 raw/dev_mp_recovery | 0 raw/dev_mp_refraction | 0 raw/dev_mp_solar | 0 raw/dev_mp_terrace | 0 raw/dev_mp_venus | 0 raw/dev_patch_code_post_gfx_mp | 0 raw/dev_patch_code_pre_gfx_mp | 0 raw/dev_patch_common_bots_mp | 0 raw/dev_patch_common_core_mp | 0 raw/dev_patch_common_mp | 0 raw/dev_patch_mp_comeback | 0 raw/dev_patch_mp_dam | 0 raw/dev_patch_mp_detroit | 0 raw/dev_patch_mp_greenband | 0 raw/dev_patch_mp_instinct | 0 raw/dev_patch_mp_lab2 | 0 raw/dev_patch_mp_laser2 | 0 raw/dev_patch_mp_levity | 0 raw/dev_patch_mp_prison | 0 raw/dev_patch_mp_recovery | 0 raw/dev_patch_mp_refraction | 0 raw/dev_patch_mp_solar | 0 raw/dev_patch_mp_terrace | 0 raw/dev_patch_mp_venus | 0 raw/dev_patch_mp_vlobby_room | 1 + raw/maps/_utility.gsc | 22030 ++++++++++++++++ .../fence_tarp_108x76_med_01.gsc | 16 + .../foliage_tree_grey_oak_lg_a.gsc | 7 + raw/maps/createart/mp_comeback_art.gsc | 13 + raw/maps/createart/mp_dam_art.gsc | 13 + raw/maps/createart/mp_detroit_art.gsc | 13 + raw/maps/createart/mp_greenband_art.gsc | 13 + raw/maps/createart/mp_instinct_art.gsc | 13 + raw/maps/createart/mp_lab2_art.gsc | 13 + raw/maps/createart/mp_laser2_art.gsc | 13 + raw/maps/createart/mp_levity_art.gsc | 13 + raw/maps/createart/mp_prison_art.gsc | 13 + raw/maps/createart/mp_prison_z_art.gsc | 13 + raw/maps/createart/mp_recovery_art.gsc | 13 + raw/maps/createart/mp_refraction_art.gsc | 13 + raw/maps/createart/mp_solar_art.gsc | 13 + raw/maps/createart/mp_terrace_art.gsc | 13 + raw/maps/createart/mp_venus_art.gsc | 13 + raw/maps/createart/mp_vlobby_room_art.gsc | 13 + raw/maps/mp/_adrenaline.gsc | 291 + raw/maps/mp/_aerial_pathnodes.gsc | 596 + raw/maps/mp/_animatedmodels.gsc | 64 + raw/maps/mp/_areas.gsc | 98 + raw/maps/mp/_art.gsc | 829 + raw/maps/mp/_audio.gsc | 871 + raw/maps/mp/_awards.gsc | 470 + raw/maps/mp/_braggingrights.gsc | 96 + raw/maps/mp/_compass.gsc | 79 + raw/maps/mp/_createfx.gsc | 72 + raw/maps/mp/_crib.gsc | 727 + raw/maps/mp/_destructables.gsc | 82 + raw/maps/mp/_dynamic_events.gsc | 337 + raw/maps/mp/_dynamic_world.gsc | 1800 ++ raw/maps/mp/_empgrenade.gsc | 232 + raw/maps/mp/_entityheadicons.gsc | 414 + raw/maps/mp/_events.gsc | 1849 ++ raw/maps/mp/_exo_battery.gsc | 239 + raw/maps/mp/_exo_cloak.gsc | 376 + raw/maps/mp/_exo_hover.gsc | 176 + raw/maps/mp/_exo_mute.gsc | 235 + raw/maps/mp/_exo_ping.gsc | 252 + raw/maps/mp/_exo_repulsor.gsc | 486 + raw/maps/mp/_exo_shield.gsc | 102 + raw/maps/mp/_exo_suit.gsc | 204 + raw/maps/mp/_exocrossbow.gsc | 137 + raw/maps/mp/_exoknife.gsc | 266 + raw/maps/mp/_explosive_drone.gsc | 1975 ++ raw/maps/mp/_explosive_gel.gsc | 431 + raw/maps/mp/_extrahealth.gsc | 296 + raw/maps/mp/_fastheal.gsc | 138 + raw/maps/mp/_flashgrenades.gsc | 175 + raw/maps/mp/_fx.gsc | 219 + raw/maps/mp/_global_fx.gsc | 43 + raw/maps/mp/_global_fx_code.gsc | 35 + raw/maps/mp/_grappling_hook.gsc | 85 + raw/maps/mp/_lasersight.gsc | 116 + raw/maps/mp/_load.gsc | 499 + raw/maps/mp/_lsrmissileguidance.gsc | 71 + raw/maps/mp/_matchdata.gsc | 1721 ++ raw/maps/mp/_menus.gsc | 3 + raw/maps/mp/_microdronelauncher.gsc | 356 + raw/maps/mp/_movers.gsc | 1396 + raw/maps/mp/_mp_lights.gsc | 613 + raw/maps/mp/_mutebomb.gsc | 157 + raw/maps/mp/_na45.gsc | 276 + raw/maps/mp/_opticsthermal.gsc | 69 + raw/maps/mp/_reinforcements.gsc | 264 + raw/maps/mp/_riotshield.gsc | 814 + raw/maps/mp/_scoreboard.gsc | 202 + raw/maps/mp/_shutter.gsc | 179 + raw/maps/mp/_snd_common_mp.gsc | 394 + raw/maps/mp/_stinger.gsc | 494 + raw/maps/mp/_stingerm7.gsc | 472 + raw/maps/mp/_stock.gsc | 46 + raw/maps/mp/_target_enhancer.gsc | 60 + raw/maps/mp/_teleport.gsc | 1457 + raw/maps/mp/_threatdetection.gsc | 1022 + raw/maps/mp/_tracking_drone.gsc | 1539 ++ raw/maps/mp/_trackrounds.gsc | 128 + raw/maps/mp/_tridrone.gsc | 716 + raw/maps/mp/_utility.gsc | 7104 +++++ raw/maps/mp/_vl_base.gsc | 556 + raw/maps/mp/_vl_camera.gsc | 4470 ++++ raw/maps/mp/_vl_firingrange.gsc | 2410 ++ raw/maps/mp/_vl_selfiebooth.gsc | 158 + raw/maps/mp/_water.gsc | 690 + raw/maps/mp/_zipline.gsc | 399 + raw/maps/mp/agents/_agent_common.gsc | 129 + raw/maps/mp/agents/_agent_utility.gsc | 458 + raw/maps/mp/agents/_agents.gsc | 557 + raw/maps/mp/agents/_agents_gametype_ball.gsc | 12 + raw/maps/mp/agents/_agents_gametype_conf.gsc | 69 + raw/maps/mp/agents/_agents_gametype_ctf.gsc | 17 + raw/maps/mp/agents/_agents_gametype_dm.gsc | 12 + raw/maps/mp/agents/_agents_gametype_dom.gsc | 49 + raw/maps/mp/agents/_agents_gametype_gun.gsc | 12 + raw/maps/mp/agents/_agents_gametype_horde.gsc | 702 + raw/maps/mp/agents/_agents_gametype_hp.gsc | 17 + .../mp/agents/_agents_gametype_infect.gsc | 12 + raw/maps/mp/agents/_agents_gametype_sd.gsc | 26 + raw/maps/mp/agents/_agents_gametype_sr.gsc | 12 + raw/maps/mp/agents/_agents_gametype_twar.gsc | 12 + .../mp/agents/_agents_gametype_vlobby.gsc | 4 + raw/maps/mp/agents/_agents_gametype_war.gsc | 12 + raw/maps/mp/agents/_scriptedagents.gsc | 247 + raw/maps/mp/agents/dog/_dog_idle.gsc | 303 + raw/maps/mp/agents/dog/_dog_melee.gsc | 350 + raw/maps/mp/agents/dog/_dog_move.gsc | 490 + raw/maps/mp/agents/dog/_dog_think.gsc | 1198 + raw/maps/mp/agents/dog/_dog_traverse.gsc | 242 + .../mp/agents/dog/_instinct_dog_think.gsc | 1265 + raw/maps/mp/bots/_bots.gsc | 3865 +++ raw/maps/mp/bots/_bots_gametype_ball.gsc | 747 + raw/maps/mp/bots/_bots_gametype_common.gsc | 531 + raw/maps/mp/bots/_bots_gametype_conf.gsc | 557 + raw/maps/mp/bots/_bots_gametype_ctf.gsc | 344 + raw/maps/mp/bots/_bots_gametype_dm.gsc | 12 + raw/maps/mp/bots/_bots_gametype_dom.gsc | 1348 + raw/maps/mp/bots/_bots_gametype_gun.gsc | 41 + raw/maps/mp/bots/_bots_gametype_horde.gsc | 15 + raw/maps/mp/bots/_bots_gametype_hp.gsc | 369 + raw/maps/mp/bots/_bots_gametype_infect.gsc | 332 + raw/maps/mp/bots/_bots_gametype_sd.gsc | 1543 ++ raw/maps/mp/bots/_bots_gametype_sr.gsc | 319 + raw/maps/mp/bots/_bots_gametype_twar.gsc | 231 + raw/maps/mp/bots/_bots_gametype_vlobby.gsc | 4 + raw/maps/mp/bots/_bots_gametype_war.gsc | 41 + raw/maps/mp/bots/_bots_ks.gsc | 874 + raw/maps/mp/bots/_bots_ks_remote_vehicle.gsc | 2439 ++ raw/maps/mp/bots/_bots_loadout.gsc | 1818 ++ raw/maps/mp/bots/_bots_personality.gsc | 974 + raw/maps/mp/bots/_bots_sentry.gsc | 380 + raw/maps/mp/bots/_bots_strategy.gsc | 2249 ++ raw/maps/mp/bots/_bots_util.gsc | 2504 ++ raw/maps/mp/gametypes/_battlebuddy.gsc | 572 + raw/maps/mp/gametypes/_battlechatter_mp.gsc | 1315 + raw/maps/mp/gametypes/_callbacksetup.gsc | 326 + raw/maps/mp/gametypes/_class.gsc | 3847 +++ raw/maps/mp/gametypes/_clientids.gsc | 1 + raw/maps/mp/gametypes/_damage.gsc | 4187 +++ raw/maps/mp/gametypes/_damagefeedback.gsc | 68 + raw/maps/mp/gametypes/_deathicons.gsc | 83 + raw/maps/mp/gametypes/_dev.gsc | 3638 +++ raw/maps/mp/gametypes/_divisions.gsc | 518 + raw/maps/mp/gametypes/_equipment.gsc | 544 + raw/maps/mp/gametypes/_friendicons.gsc | 115 + raw/maps/mp/gametypes/_gamelogic.gsc | 3987 +++ raw/maps/mp/gametypes/_gameobjects.gsc | 3056 +++ raw/maps/mp/gametypes/_gamescore.gsc | 488 + raw/maps/mp/gametypes/_globalentities.gsc | 4 + raw/maps/mp/gametypes/_globallogic.gsc | 263 + raw/maps/mp/gametypes/_hardpoints.gsc | 0 raw/maps/mp/gametypes/_healthoverlay.gsc | 369 + raw/maps/mp/gametypes/_high_jump_mp.gsc | 162 + raw/maps/mp/gametypes/_hostmigration.gsc | 261 + raw/maps/mp/gametypes/_hud.gsc | 149 + raw/maps/mp/gametypes/_hud_message.gsc | 1251 + raw/maps/mp/gametypes/_hud_util.gsc | 1024 + raw/maps/mp/gametypes/_killcam.gsc | 717 + raw/maps/mp/gametypes/_menus.gsc | 794 + raw/maps/mp/gametypes/_missions.gsc | 4824 ++++ raw/maps/mp/gametypes/_music_and_dialog.gsc | 708 + raw/maps/mp/gametypes/_objpoints.gsc | 162 + raw/maps/mp/gametypes/_orbital.gsc | 2051 ++ raw/maps/mp/gametypes/_persistence.gsc | 653 + .../mp/gametypes/_player_boost_jump_mp.gsc | 48 + raw/maps/mp/gametypes/_playercards.gsc | 29 + raw/maps/mp/gametypes/_playerlogic.gsc | 2897 ++ raw/maps/mp/gametypes/_portable_radar.gsc | 248 + raw/maps/mp/gametypes/_quickmessages.gsc | 267 + raw/maps/mp/gametypes/_rank.gsc | 591 + raw/maps/mp/gametypes/_scrambler.gsc | 473 + raw/maps/mp/gametypes/_serversettings.gsc | 107 + raw/maps/mp/gametypes/_shellshock.gsc | 286 + raw/maps/mp/gametypes/_spawnfactor.gsc | 543 + raw/maps/mp/gametypes/_spawnlogic.gsc | 1078 + raw/maps/mp/gametypes/_spawnscoring.gsc | 1361 + raw/maps/mp/gametypes/_spectating.gsc | 333 + raw/maps/mp/gametypes/_teams.gsc | 1201 + raw/maps/mp/gametypes/_tweakables.gsc | 371 + raw/maps/mp/gametypes/_weapons.gsc | 5136 ++++ raw/maps/mp/gametypes/ball.gsc | 2309 ++ raw/maps/mp/gametypes/common_sd_sr.gsc | 1080 + raw/maps/mp/gametypes/conf.gsc | 391 + raw/maps/mp/gametypes/ctf.gsc | 1568 ++ raw/maps/mp/gametypes/dm.gsc | 189 + raw/maps/mp/gametypes/dom.gsc | 1336 + raw/maps/mp/gametypes/gun.gsc | 564 + raw/maps/mp/gametypes/hp.gsc | 1102 + raw/maps/mp/gametypes/infect.gsc | 984 + raw/maps/mp/gametypes/sd.gsc | 292 + raw/maps/mp/gametypes/sr.gsc | 613 + raw/maps/mp/gametypes/twar.gsc | 2206 ++ raw/maps/mp/gametypes/vlobby.gsc | 167 + raw/maps/mp/gametypes/war.gsc | 225 + raw/maps/mp/killstreaks/_aerial_utility.gsc | 1567 ++ raw/maps/mp/killstreaks/_agent_killstreak.gsc | 203 + raw/maps/mp/killstreaks/_airdrop.gsc | 1955 ++ raw/maps/mp/killstreaks/_airstrike.gsc | 1410 + raw/maps/mp/killstreaks/_assaultdrone_ai.gsc | 843 + raw/maps/mp/killstreaks/_autosentry.gsc | 1550 ++ raw/maps/mp/killstreaks/_coop_util.gsc | 680 + raw/maps/mp/killstreaks/_dog_killstreak.gsc | 286 + raw/maps/mp/killstreaks/_drone_assault.gsc | 1148 + .../mp/killstreaks/_drone_carepackage.gsc | 181 + raw/maps/mp/killstreaks/_drone_common.gsc | 484 + raw/maps/mp/killstreaks/_drone_recon.gsc | 716 + raw/maps/mp/killstreaks/_emp.gsc | 975 + raw/maps/mp/killstreaks/_juggernaut.gsc | 3455 +++ raw/maps/mp/killstreaks/_killstreaks.gsc | 2524 ++ raw/maps/mp/killstreaks/_killstreaks_init.gsc | 125 + raw/maps/mp/killstreaks/_marking_util.gsc | 10 + raw/maps/mp/killstreaks/_missile_strike.gsc | 1657 ++ raw/maps/mp/killstreaks/_nuke.gsc | 520 + .../mp/killstreaks/_orbital_carepackage.gsc | 984 + raw/maps/mp/killstreaks/_orbital_strike.gsc | 1632 ++ raw/maps/mp/killstreaks/_orbital_util.gsc | 945 + raw/maps/mp/killstreaks/_orbitalsupport.gsc | 2426 ++ raw/maps/mp/killstreaks/_placeable.gsc | 700 + raw/maps/mp/killstreaks/_remoteturret.gsc | 2294 ++ raw/maps/mp/killstreaks/_rippedturret.gsc | 322 + raw/maps/mp/killstreaks/_teamammorefill.gsc | 61 + raw/maps/mp/killstreaks/_uav.gsc | 872 + raw/maps/mp/killstreaks/_warbird.gsc | 2288 ++ .../mp/killstreaks/streak_mp_comeback.gsc | 343 + raw/maps/mp/killstreaks/streak_mp_dam.gsc | 609 + raw/maps/mp/killstreaks/streak_mp_detroit.gsc | 323 + .../mp/killstreaks/streak_mp_instinct.gsc | 326 + raw/maps/mp/killstreaks/streak_mp_laser2.gsc | 1763 ++ raw/maps/mp/killstreaks/streak_mp_prison.gsc | 994 + .../mp/killstreaks/streak_mp_recovery.gsc | 621 + .../mp/killstreaks/streak_mp_refraction.gsc | 751 + raw/maps/mp/killstreaks/streak_mp_solar.gsc | 629 + raw/maps/mp/killstreaks/streak_mp_terrace.gsc | 19 + raw/maps/mp/mp_comeback.gsc | 101 + raw/maps/mp/mp_comeback_aud.gsc | 12 + raw/maps/mp/mp_comeback_lighting.gsc | 41 + raw/maps/mp/mp_comeback_precache.gsc | 4 + raw/maps/mp/mp_dam.gsc | 1385 + raw/maps/mp/mp_dam_lighting.gsc | 20 + raw/maps/mp/mp_dam_precache.gsc | 7 + raw/maps/mp/mp_detroit.gsc | 119 + raw/maps/mp/mp_detroit_aud.gsc | 13 + raw/maps/mp/mp_detroit_events.gsc | 429 + raw/maps/mp/mp_detroit_lighting.gsc | 18 + raw/maps/mp/mp_detroit_precache.gsc | 4 + raw/maps/mp/mp_greenband.gsc | 287 + raw/maps/mp/mp_greenband_precache.gsc | 4 + raw/maps/mp/mp_instinct.gsc | 1012 + raw/maps/mp/mp_instinct_aud.gsc | 11 + raw/maps/mp/mp_instinct_lighting.gsc | 46 + raw/maps/mp/mp_instinct_precache.gsc | 4 + raw/maps/mp/mp_lab2.gsc | 2114 ++ raw/maps/mp/mp_lab2_precache.gsc | 4 + raw/maps/mp/mp_laser2.gsc | 1354 + raw/maps/mp/mp_laser2_aud.gsc | 75 + raw/maps/mp/mp_laser2_lighting.gsc | 41 + raw/maps/mp/mp_laser2_precache.gsc | 4 + raw/maps/mp/mp_levity.gsc | 404 + raw/maps/mp/mp_levity_aud.gsc | 31 + raw/maps/mp/mp_levity_lighting.gsc | 39 + raw/maps/mp/mp_levity_precache.gsc | 4 + raw/maps/mp/mp_prison.gsc | 927 + raw/maps/mp/mp_prison_lighting.gsc | 36 + raw/maps/mp/mp_prison_precache.gsc | 6 + raw/maps/mp/mp_prison_z.gsc | 494 + raw/maps/mp/mp_prison_z_lighting.gsc | 41 + raw/maps/mp/mp_prison_z_precache.gsc | 6 + raw/maps/mp/mp_recovery.gsc | 1121 + raw/maps/mp/mp_recovery_aud.gsc | 11 + raw/maps/mp/mp_recovery_lighting.gsc | 15 + raw/maps/mp/mp_recovery_precache.gsc | 6 + raw/maps/mp/mp_refraction.gsc | 1137 + raw/maps/mp/mp_refraction_lighting.gsc | 24 + raw/maps/mp/mp_refraction_precache.gsc | 4 + raw/maps/mp/mp_solar.gsc | 82 + raw/maps/mp/mp_solar_aud.gsc | 72 + raw/maps/mp/mp_solar_lighting.gsc | 53 + raw/maps/mp/mp_solar_precache.gsc | 4 + raw/maps/mp/mp_terrace.gsc | 123 + raw/maps/mp/mp_terrace_aud.gsc | 13 + raw/maps/mp/mp_terrace_lighting.gsc | 38 + raw/maps/mp/mp_terrace_precache.gsc | 4 + raw/maps/mp/mp_venus.gsc | 121 + raw/maps/mp/mp_venus_aud.gsc | 11 + raw/maps/mp/mp_venus_lighting.gsc | 23 + raw/maps/mp/mp_venus_precache.gsc | 4 + raw/maps/mp/mp_vlobby_room.gsc | 243 + raw/maps/mp/mp_vlobby_room_aud.gsc | 11 + raw/maps/mp/mp_vlobby_room_lighting.gsc | 56 + raw/maps/mp/mp_vlobby_room_precache.gsc | 4 + raw/maps/mp/perks/_perkfunctions.gsc | 1378 + raw/maps/mp/perks/_perks.gsc | 781 + raw/mptype/mptype_cloak_test.gsc | 6 + raw/radiant/keys.txt | 5560 ++++ raw/xmodelalias/alias_cloak_test.gsc | 6 + raw/xmodelalias/alias_mp_sentinel_heads.gsc | 8 + 359 files changed, 242229 insertions(+) create mode 100644 LICENSE create mode 100644 raw/character/mp_character_cloak_test.gsc create mode 100644 raw/character/mp_character_sentinel.gsc create mode 100644 raw/codescripts/character.gsc create mode 100644 raw/codescripts/delete.gsc create mode 100644 raw/codescripts/struct.gsc create mode 100644 raw/common_scripts/_artcommon.gsc create mode 100644 raw/common_scripts/_bcs_location_trigs.gsc create mode 100644 raw/common_scripts/_createfx.gsc create mode 100644 raw/common_scripts/_createfxmenu.gsc create mode 100644 raw/common_scripts/_destructible.gsc create mode 100644 raw/common_scripts/_dynamic_world.gsc create mode 100644 raw/common_scripts/_elevator.gsc create mode 100644 raw/common_scripts/_exploder.gsc create mode 100644 raw/common_scripts/_fx.gsc create mode 100644 raw/common_scripts/utility.gsc create mode 100644 raw/destructible_scripts/berlin_hotel_lights_wall2.gsc create mode 100644 raw/destructible_scripts/container_plastic_72x56x48_01_destp.gsc create mode 100644 raw/destructible_scripts/toy_chicken.gsc create mode 100644 raw/destructible_scripts/toy_electricbox4.gsc create mode 100644 raw/destructible_scripts/toy_locker_double.gsc create mode 100644 raw/dev_code_post_gfx_mp create mode 100644 raw/dev_code_pre_gfx_mp create mode 100644 raw/dev_common_bots_mp create mode 100644 raw/dev_common_core_mp create mode 100644 raw/dev_common_mp create mode 100644 raw/dev_development_mp create mode 100644 raw/dev_mp_comeback create mode 100644 raw/dev_mp_dam create mode 100644 raw/dev_mp_detroit create mode 100644 raw/dev_mp_greenband create mode 100644 raw/dev_mp_instinct create mode 100644 raw/dev_mp_lab2 create mode 100644 raw/dev_mp_laser2 create mode 100644 raw/dev_mp_levity create mode 100644 raw/dev_mp_prison create mode 100644 raw/dev_mp_prison_z create mode 100644 raw/dev_mp_recovery create mode 100644 raw/dev_mp_refraction create mode 100644 raw/dev_mp_solar create mode 100644 raw/dev_mp_terrace create mode 100644 raw/dev_mp_venus create mode 100644 raw/dev_patch_code_post_gfx_mp create mode 100644 raw/dev_patch_code_pre_gfx_mp create mode 100644 raw/dev_patch_common_bots_mp create mode 100644 raw/dev_patch_common_core_mp create mode 100644 raw/dev_patch_common_mp create mode 100644 raw/dev_patch_mp_comeback create mode 100644 raw/dev_patch_mp_dam create mode 100644 raw/dev_patch_mp_detroit create mode 100644 raw/dev_patch_mp_greenband create mode 100644 raw/dev_patch_mp_instinct create mode 100644 raw/dev_patch_mp_lab2 create mode 100644 raw/dev_patch_mp_laser2 create mode 100644 raw/dev_patch_mp_levity create mode 100644 raw/dev_patch_mp_prison create mode 100644 raw/dev_patch_mp_recovery create mode 100644 raw/dev_patch_mp_refraction create mode 100644 raw/dev_patch_mp_solar create mode 100644 raw/dev_patch_mp_terrace create mode 100644 raw/dev_patch_mp_venus create mode 100644 raw/dev_patch_mp_vlobby_room create mode 100644 raw/maps/_utility.gsc create mode 100644 raw/maps/animated_models/fence_tarp_108x76_med_01.gsc create mode 100644 raw/maps/animated_models/foliage_tree_grey_oak_lg_a.gsc create mode 100644 raw/maps/createart/mp_comeback_art.gsc create mode 100644 raw/maps/createart/mp_dam_art.gsc create mode 100644 raw/maps/createart/mp_detroit_art.gsc create mode 100644 raw/maps/createart/mp_greenband_art.gsc create mode 100644 raw/maps/createart/mp_instinct_art.gsc create mode 100644 raw/maps/createart/mp_lab2_art.gsc create mode 100644 raw/maps/createart/mp_laser2_art.gsc create mode 100644 raw/maps/createart/mp_levity_art.gsc create mode 100644 raw/maps/createart/mp_prison_art.gsc create mode 100644 raw/maps/createart/mp_prison_z_art.gsc create mode 100644 raw/maps/createart/mp_recovery_art.gsc create mode 100644 raw/maps/createart/mp_refraction_art.gsc create mode 100644 raw/maps/createart/mp_solar_art.gsc create mode 100644 raw/maps/createart/mp_terrace_art.gsc create mode 100644 raw/maps/createart/mp_venus_art.gsc create mode 100644 raw/maps/createart/mp_vlobby_room_art.gsc create mode 100644 raw/maps/mp/_adrenaline.gsc create mode 100644 raw/maps/mp/_aerial_pathnodes.gsc create mode 100644 raw/maps/mp/_animatedmodels.gsc create mode 100644 raw/maps/mp/_areas.gsc create mode 100644 raw/maps/mp/_art.gsc create mode 100644 raw/maps/mp/_audio.gsc create mode 100644 raw/maps/mp/_awards.gsc create mode 100644 raw/maps/mp/_braggingrights.gsc create mode 100644 raw/maps/mp/_compass.gsc create mode 100644 raw/maps/mp/_createfx.gsc create mode 100644 raw/maps/mp/_crib.gsc create mode 100644 raw/maps/mp/_destructables.gsc create mode 100644 raw/maps/mp/_dynamic_events.gsc create mode 100644 raw/maps/mp/_dynamic_world.gsc create mode 100644 raw/maps/mp/_empgrenade.gsc create mode 100644 raw/maps/mp/_entityheadicons.gsc create mode 100644 raw/maps/mp/_events.gsc create mode 100644 raw/maps/mp/_exo_battery.gsc create mode 100644 raw/maps/mp/_exo_cloak.gsc create mode 100644 raw/maps/mp/_exo_hover.gsc create mode 100644 raw/maps/mp/_exo_mute.gsc create mode 100644 raw/maps/mp/_exo_ping.gsc create mode 100644 raw/maps/mp/_exo_repulsor.gsc create mode 100644 raw/maps/mp/_exo_shield.gsc create mode 100644 raw/maps/mp/_exo_suit.gsc create mode 100644 raw/maps/mp/_exocrossbow.gsc create mode 100644 raw/maps/mp/_exoknife.gsc create mode 100644 raw/maps/mp/_explosive_drone.gsc create mode 100644 raw/maps/mp/_explosive_gel.gsc create mode 100644 raw/maps/mp/_extrahealth.gsc create mode 100644 raw/maps/mp/_fastheal.gsc create mode 100644 raw/maps/mp/_flashgrenades.gsc create mode 100644 raw/maps/mp/_fx.gsc create mode 100644 raw/maps/mp/_global_fx.gsc create mode 100644 raw/maps/mp/_global_fx_code.gsc create mode 100644 raw/maps/mp/_grappling_hook.gsc create mode 100644 raw/maps/mp/_lasersight.gsc create mode 100644 raw/maps/mp/_load.gsc create mode 100644 raw/maps/mp/_lsrmissileguidance.gsc create mode 100644 raw/maps/mp/_matchdata.gsc create mode 100644 raw/maps/mp/_menus.gsc create mode 100644 raw/maps/mp/_microdronelauncher.gsc create mode 100644 raw/maps/mp/_movers.gsc create mode 100644 raw/maps/mp/_mp_lights.gsc create mode 100644 raw/maps/mp/_mutebomb.gsc create mode 100644 raw/maps/mp/_na45.gsc create mode 100644 raw/maps/mp/_opticsthermal.gsc create mode 100644 raw/maps/mp/_reinforcements.gsc create mode 100644 raw/maps/mp/_riotshield.gsc create mode 100644 raw/maps/mp/_scoreboard.gsc create mode 100644 raw/maps/mp/_shutter.gsc create mode 100644 raw/maps/mp/_snd_common_mp.gsc create mode 100644 raw/maps/mp/_stinger.gsc create mode 100644 raw/maps/mp/_stingerm7.gsc create mode 100644 raw/maps/mp/_stock.gsc create mode 100644 raw/maps/mp/_target_enhancer.gsc create mode 100644 raw/maps/mp/_teleport.gsc create mode 100644 raw/maps/mp/_threatdetection.gsc create mode 100644 raw/maps/mp/_tracking_drone.gsc create mode 100644 raw/maps/mp/_trackrounds.gsc create mode 100644 raw/maps/mp/_tridrone.gsc create mode 100644 raw/maps/mp/_utility.gsc create mode 100644 raw/maps/mp/_vl_base.gsc create mode 100644 raw/maps/mp/_vl_camera.gsc create mode 100644 raw/maps/mp/_vl_firingrange.gsc create mode 100644 raw/maps/mp/_vl_selfiebooth.gsc create mode 100644 raw/maps/mp/_water.gsc create mode 100644 raw/maps/mp/_zipline.gsc create mode 100644 raw/maps/mp/agents/_agent_common.gsc create mode 100644 raw/maps/mp/agents/_agent_utility.gsc create mode 100644 raw/maps/mp/agents/_agents.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_ball.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_conf.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_ctf.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_dm.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_dom.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_gun.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_horde.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_hp.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_infect.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_sd.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_sr.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_twar.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_vlobby.gsc create mode 100644 raw/maps/mp/agents/_agents_gametype_war.gsc create mode 100644 raw/maps/mp/agents/_scriptedagents.gsc create mode 100644 raw/maps/mp/agents/dog/_dog_idle.gsc create mode 100644 raw/maps/mp/agents/dog/_dog_melee.gsc create mode 100644 raw/maps/mp/agents/dog/_dog_move.gsc create mode 100644 raw/maps/mp/agents/dog/_dog_think.gsc create mode 100644 raw/maps/mp/agents/dog/_dog_traverse.gsc create mode 100644 raw/maps/mp/agents/dog/_instinct_dog_think.gsc create mode 100644 raw/maps/mp/bots/_bots.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_ball.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_common.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_conf.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_ctf.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_dm.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_dom.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_gun.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_horde.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_hp.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_infect.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_sd.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_sr.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_twar.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_vlobby.gsc create mode 100644 raw/maps/mp/bots/_bots_gametype_war.gsc create mode 100644 raw/maps/mp/bots/_bots_ks.gsc create mode 100644 raw/maps/mp/bots/_bots_ks_remote_vehicle.gsc create mode 100644 raw/maps/mp/bots/_bots_loadout.gsc create mode 100644 raw/maps/mp/bots/_bots_personality.gsc create mode 100644 raw/maps/mp/bots/_bots_sentry.gsc create mode 100644 raw/maps/mp/bots/_bots_strategy.gsc create mode 100644 raw/maps/mp/bots/_bots_util.gsc create mode 100644 raw/maps/mp/gametypes/_battlebuddy.gsc create mode 100644 raw/maps/mp/gametypes/_battlechatter_mp.gsc create mode 100644 raw/maps/mp/gametypes/_callbacksetup.gsc create mode 100644 raw/maps/mp/gametypes/_class.gsc create mode 100644 raw/maps/mp/gametypes/_clientids.gsc create mode 100644 raw/maps/mp/gametypes/_damage.gsc create mode 100644 raw/maps/mp/gametypes/_damagefeedback.gsc create mode 100644 raw/maps/mp/gametypes/_deathicons.gsc create mode 100644 raw/maps/mp/gametypes/_dev.gsc create mode 100644 raw/maps/mp/gametypes/_divisions.gsc create mode 100644 raw/maps/mp/gametypes/_equipment.gsc create mode 100644 raw/maps/mp/gametypes/_friendicons.gsc create mode 100644 raw/maps/mp/gametypes/_gamelogic.gsc create mode 100644 raw/maps/mp/gametypes/_gameobjects.gsc create mode 100644 raw/maps/mp/gametypes/_gamescore.gsc create mode 100644 raw/maps/mp/gametypes/_globalentities.gsc create mode 100644 raw/maps/mp/gametypes/_globallogic.gsc create mode 100644 raw/maps/mp/gametypes/_hardpoints.gsc create mode 100644 raw/maps/mp/gametypes/_healthoverlay.gsc create mode 100644 raw/maps/mp/gametypes/_high_jump_mp.gsc create mode 100644 raw/maps/mp/gametypes/_hostmigration.gsc create mode 100644 raw/maps/mp/gametypes/_hud.gsc create mode 100644 raw/maps/mp/gametypes/_hud_message.gsc create mode 100644 raw/maps/mp/gametypes/_hud_util.gsc create mode 100644 raw/maps/mp/gametypes/_killcam.gsc create mode 100644 raw/maps/mp/gametypes/_menus.gsc create mode 100644 raw/maps/mp/gametypes/_missions.gsc create mode 100644 raw/maps/mp/gametypes/_music_and_dialog.gsc create mode 100644 raw/maps/mp/gametypes/_objpoints.gsc create mode 100644 raw/maps/mp/gametypes/_orbital.gsc create mode 100644 raw/maps/mp/gametypes/_persistence.gsc create mode 100644 raw/maps/mp/gametypes/_player_boost_jump_mp.gsc create mode 100644 raw/maps/mp/gametypes/_playercards.gsc create mode 100644 raw/maps/mp/gametypes/_playerlogic.gsc create mode 100644 raw/maps/mp/gametypes/_portable_radar.gsc create mode 100644 raw/maps/mp/gametypes/_quickmessages.gsc create mode 100644 raw/maps/mp/gametypes/_rank.gsc create mode 100644 raw/maps/mp/gametypes/_scrambler.gsc create mode 100644 raw/maps/mp/gametypes/_serversettings.gsc create mode 100644 raw/maps/mp/gametypes/_shellshock.gsc create mode 100644 raw/maps/mp/gametypes/_spawnfactor.gsc create mode 100644 raw/maps/mp/gametypes/_spawnlogic.gsc create mode 100644 raw/maps/mp/gametypes/_spawnscoring.gsc create mode 100644 raw/maps/mp/gametypes/_spectating.gsc create mode 100644 raw/maps/mp/gametypes/_teams.gsc create mode 100644 raw/maps/mp/gametypes/_tweakables.gsc create mode 100644 raw/maps/mp/gametypes/_weapons.gsc create mode 100644 raw/maps/mp/gametypes/ball.gsc create mode 100644 raw/maps/mp/gametypes/common_sd_sr.gsc create mode 100644 raw/maps/mp/gametypes/conf.gsc create mode 100644 raw/maps/mp/gametypes/ctf.gsc create mode 100644 raw/maps/mp/gametypes/dm.gsc create mode 100644 raw/maps/mp/gametypes/dom.gsc create mode 100644 raw/maps/mp/gametypes/gun.gsc create mode 100644 raw/maps/mp/gametypes/hp.gsc create mode 100644 raw/maps/mp/gametypes/infect.gsc create mode 100644 raw/maps/mp/gametypes/sd.gsc create mode 100644 raw/maps/mp/gametypes/sr.gsc create mode 100644 raw/maps/mp/gametypes/twar.gsc create mode 100644 raw/maps/mp/gametypes/vlobby.gsc create mode 100644 raw/maps/mp/gametypes/war.gsc create mode 100644 raw/maps/mp/killstreaks/_aerial_utility.gsc create mode 100644 raw/maps/mp/killstreaks/_agent_killstreak.gsc create mode 100644 raw/maps/mp/killstreaks/_airdrop.gsc create mode 100644 raw/maps/mp/killstreaks/_airstrike.gsc create mode 100644 raw/maps/mp/killstreaks/_assaultdrone_ai.gsc create mode 100644 raw/maps/mp/killstreaks/_autosentry.gsc create mode 100644 raw/maps/mp/killstreaks/_coop_util.gsc create mode 100644 raw/maps/mp/killstreaks/_dog_killstreak.gsc create mode 100644 raw/maps/mp/killstreaks/_drone_assault.gsc create mode 100644 raw/maps/mp/killstreaks/_drone_carepackage.gsc create mode 100644 raw/maps/mp/killstreaks/_drone_common.gsc create mode 100644 raw/maps/mp/killstreaks/_drone_recon.gsc create mode 100644 raw/maps/mp/killstreaks/_emp.gsc create mode 100644 raw/maps/mp/killstreaks/_juggernaut.gsc create mode 100644 raw/maps/mp/killstreaks/_killstreaks.gsc create mode 100644 raw/maps/mp/killstreaks/_killstreaks_init.gsc create mode 100644 raw/maps/mp/killstreaks/_marking_util.gsc create mode 100644 raw/maps/mp/killstreaks/_missile_strike.gsc create mode 100644 raw/maps/mp/killstreaks/_nuke.gsc create mode 100644 raw/maps/mp/killstreaks/_orbital_carepackage.gsc create mode 100644 raw/maps/mp/killstreaks/_orbital_strike.gsc create mode 100644 raw/maps/mp/killstreaks/_orbital_util.gsc create mode 100644 raw/maps/mp/killstreaks/_orbitalsupport.gsc create mode 100644 raw/maps/mp/killstreaks/_placeable.gsc create mode 100644 raw/maps/mp/killstreaks/_remoteturret.gsc create mode 100644 raw/maps/mp/killstreaks/_rippedturret.gsc create mode 100644 raw/maps/mp/killstreaks/_teamammorefill.gsc create mode 100644 raw/maps/mp/killstreaks/_uav.gsc create mode 100644 raw/maps/mp/killstreaks/_warbird.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_comeback.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_dam.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_detroit.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_instinct.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_laser2.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_prison.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_recovery.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_refraction.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_solar.gsc create mode 100644 raw/maps/mp/killstreaks/streak_mp_terrace.gsc create mode 100644 raw/maps/mp/mp_comeback.gsc create mode 100644 raw/maps/mp/mp_comeback_aud.gsc create mode 100644 raw/maps/mp/mp_comeback_lighting.gsc create mode 100644 raw/maps/mp/mp_comeback_precache.gsc create mode 100644 raw/maps/mp/mp_dam.gsc create mode 100644 raw/maps/mp/mp_dam_lighting.gsc create mode 100644 raw/maps/mp/mp_dam_precache.gsc create mode 100644 raw/maps/mp/mp_detroit.gsc create mode 100644 raw/maps/mp/mp_detroit_aud.gsc create mode 100644 raw/maps/mp/mp_detroit_events.gsc create mode 100644 raw/maps/mp/mp_detroit_lighting.gsc create mode 100644 raw/maps/mp/mp_detroit_precache.gsc create mode 100644 raw/maps/mp/mp_greenband.gsc create mode 100644 raw/maps/mp/mp_greenband_precache.gsc create mode 100644 raw/maps/mp/mp_instinct.gsc create mode 100644 raw/maps/mp/mp_instinct_aud.gsc create mode 100644 raw/maps/mp/mp_instinct_lighting.gsc create mode 100644 raw/maps/mp/mp_instinct_precache.gsc create mode 100644 raw/maps/mp/mp_lab2.gsc create mode 100644 raw/maps/mp/mp_lab2_precache.gsc create mode 100644 raw/maps/mp/mp_laser2.gsc create mode 100644 raw/maps/mp/mp_laser2_aud.gsc create mode 100644 raw/maps/mp/mp_laser2_lighting.gsc create mode 100644 raw/maps/mp/mp_laser2_precache.gsc create mode 100644 raw/maps/mp/mp_levity.gsc create mode 100644 raw/maps/mp/mp_levity_aud.gsc create mode 100644 raw/maps/mp/mp_levity_lighting.gsc create mode 100644 raw/maps/mp/mp_levity_precache.gsc create mode 100644 raw/maps/mp/mp_prison.gsc create mode 100644 raw/maps/mp/mp_prison_lighting.gsc create mode 100644 raw/maps/mp/mp_prison_precache.gsc create mode 100644 raw/maps/mp/mp_prison_z.gsc create mode 100644 raw/maps/mp/mp_prison_z_lighting.gsc create mode 100644 raw/maps/mp/mp_prison_z_precache.gsc create mode 100644 raw/maps/mp/mp_recovery.gsc create mode 100644 raw/maps/mp/mp_recovery_aud.gsc create mode 100644 raw/maps/mp/mp_recovery_lighting.gsc create mode 100644 raw/maps/mp/mp_recovery_precache.gsc create mode 100644 raw/maps/mp/mp_refraction.gsc create mode 100644 raw/maps/mp/mp_refraction_lighting.gsc create mode 100644 raw/maps/mp/mp_refraction_precache.gsc create mode 100644 raw/maps/mp/mp_solar.gsc create mode 100644 raw/maps/mp/mp_solar_aud.gsc create mode 100644 raw/maps/mp/mp_solar_lighting.gsc create mode 100644 raw/maps/mp/mp_solar_precache.gsc create mode 100644 raw/maps/mp/mp_terrace.gsc create mode 100644 raw/maps/mp/mp_terrace_aud.gsc create mode 100644 raw/maps/mp/mp_terrace_lighting.gsc create mode 100644 raw/maps/mp/mp_terrace_precache.gsc create mode 100644 raw/maps/mp/mp_venus.gsc create mode 100644 raw/maps/mp/mp_venus_aud.gsc create mode 100644 raw/maps/mp/mp_venus_lighting.gsc create mode 100644 raw/maps/mp/mp_venus_precache.gsc create mode 100644 raw/maps/mp/mp_vlobby_room.gsc create mode 100644 raw/maps/mp/mp_vlobby_room_aud.gsc create mode 100644 raw/maps/mp/mp_vlobby_room_lighting.gsc create mode 100644 raw/maps/mp/mp_vlobby_room_precache.gsc create mode 100644 raw/maps/mp/perks/_perkfunctions.gsc create mode 100644 raw/maps/mp/perks/_perks.gsc create mode 100644 raw/mptype/mptype_cloak_test.gsc create mode 100644 raw/radiant/keys.txt create mode 100644 raw/xmodelalias/alias_cloak_test.gsc create mode 100644 raw/xmodelalias/alias_mp_sentinel_heads.gsc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0e259d4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/raw/character/mp_character_cloak_test.gsc b/raw/character/mp_character_cloak_test.gsc new file mode 100644 index 0000000..89c5e0f --- /dev/null +++ b/raw/character/mp_character_cloak_test.gsc @@ -0,0 +1,17 @@ +// THIS FILE IS AUTOGENERATED, DO NOT MODIFY +main() +{ + self setModel("mp_body_cloak_test"); + self attach("mp_head_cloak_test", "", true); + self.headModel = "mp_head_cloak_test"; + self setViewmodel("mp_viewhands_cloak_test"); + self.voice = "american"; + self SetClothType("vestlight"); +} + +precache() +{ + precacheModel("mp_body_cloak_test"); + precacheModel("mp_head_cloak_test"); + precacheModel("mp_viewhands_cloak_test"); +} diff --git a/raw/character/mp_character_sentinel.gsc b/raw/character/mp_character_sentinel.gsc new file mode 100644 index 0000000..93714cf --- /dev/null +++ b/raw/character/mp_character_sentinel.gsc @@ -0,0 +1,16 @@ +// THIS FILE IS AUTOGENERATED, DO NOT MODIFY +main() +{ + self setModel("mp_sentinel_body_nojet_b"); + codescripts\character::attachHead( "alias_mp_sentinel_heads", xmodelalias\alias_mp_sentinel_heads::main() ); + self setViewmodel("viewhands_s1_pmc"); + self.voice = "american"; + self SetClothType("vestlight"); +} + +precache() +{ + precacheModel("mp_sentinel_body_nojet_b"); + codescripts\character::precacheModelArray(xmodelalias\alias_mp_sentinel_heads::main()); + precacheModel("viewhands_s1_pmc"); +} diff --git a/raw/codescripts/character.gsc b/raw/codescripts/character.gsc new file mode 100644 index 0000000..799106f --- /dev/null +++ b/raw/codescripts/character.gsc @@ -0,0 +1,281 @@ +setModelFromArray( a ) +{ + self setModel( a[ randomint( a.size ) ] ); +} + +precacheModelArray( a ) +{ + for ( i = 0; i < a.size; i++ ) + precacheModel( a[ i ] ); +} + +attachHead( headAlias, headArray ) +{ +/# + // For test map purposes only!! - IW5_Characters uses this. + if ( IsDefined( level.store_characterinfo ) ) + { + if ( !IsDefined( self.characterinfo ) ) + { + self.characterinfo = SpawnStruct(); + self.characterinfo.headalias = headAlias; + self.characterinfo.headarray = headarray; + } + } +#/ + + if ( !isdefined( level.character_head_index ) ) + level.character_head_index = []; + + if ( !isdefined( level.character_head_index[ headAlias ] ) ) + level.character_head_index[ headAlias ] = randomint( headArray.size ); + + assert( level.character_head_index[ headAlias ] < headArray.size ); + + index = ( level.character_head_index[ headAlias ] + 1 ) % headArray.size; + + // the designer can overwrite the character + if ( isdefined( self.script_char_index ) ) + { + index = self.script_char_index % headArray.size; + } + + level.character_head_index[ headAlias ] = index; + + self setHeadModel( headArray[ index ] ); +} + +setHeadModel( headmodel ) +{ + if (isdefined(self.headmodel)) + self detach(self.headmodel); + + self attach( headmodel, "", true ); + self.headModel = headmodel; +} + +attachHat( hatAlias, hatArray ) +{ + if ( !isdefined( level.character_hat_index ) ) + level.character_hat_index = []; + + if ( !isdefined( level.character_hat_index[ hatAlias ] ) ) + level.character_hat_index[ hatAlias ] = randomint( hatArray.size ); + + assert( level.character_hat_index[ hatAlias ] < hatArray.size ); + + index = ( level.character_hat_index[ hatAlias ] + 1 ) % hatArray.size; + + level.character_hat_index[ hatAlias ] = index; + + self attach( hatArray[ index ] ); + self.hatModel = hatArray[ index ]; +} + +new() +{ + self detachAll(); + oldGunHand = self.anim_gunHand; + if ( !isdefined( oldGunHand ) ) + return; + self.anim_gunHand = "none"; + self [[ anim.PutGunInHand ]]( oldGunHand ); +} + +save() +{ + info[ "gunHand" ] = self.anim_gunHand; + info[ "gunInHand" ] = self.anim_gunInHand; + info[ "model" ] = self.model; + info[ "hatModel" ] = self.hatModel; + if ( isdefined( self.name ) ) + { + info[ "name" ] = self.name; + println( "Save: Guy has name ", self.name ); + } + else + println( "save: Guy had no name!" ); + + attachSize = self getAttachSize(); + for ( i = 0; i < attachSize; i++ ) + { + info[ "attach" ][ i ][ "model" ] = self getAttachModelName( i ); + info[ "attach" ][ i ][ "tag" ] = self getAttachTagName( i ); + } + return info; +} + +load( info ) +{ + self detachAll(); + self.anim_gunHand = info[ "gunHand" ]; + self.anim_gunInHand = info[ "gunInHand" ]; + self setModel( info[ "model" ] ); + self.hatModel = info[ "hatModel" ]; + if ( isdefined( info[ "name" ] ) ) + { + self.name = info[ "name" ]; + println( "Load: Guy has name ", self.name ); + } + else + println( "Load: Guy had no name!" ); + + attachInfo = info[ "attach" ]; + attachSize = attachInfo.size; + for ( i = 0; i < attachSize; i++ ) + self attach( attachInfo[ i ][ "model" ], attachInfo[ i ][ "tag" ] ); +} + +precache( info ) +{ + if ( isdefined( info[ "name" ] ) ) + println( "Precache: Guy has name ", info[ "name" ] ); + else + println( "Precache: Guy had no name!" ); + + precacheModel( info[ "model" ] ); + + attachInfo = info[ "attach" ]; + attachSize = attachInfo.size; + for ( i = 0; i < attachSize; i++ ) + precacheModel( attachInfo[ i ][ "model" ] ); +} + +/* +sample save / precache / load usage( precache is only required if there are any waits in the level script before load ): + +save: + info = foley codescripts\character::save(); + game[ "foley" ] = info; + changelevel( "burnville", 0, true ); + +precache: + codescripts\character::precache( game[ "foley" ] ); + +load: + foley codescripts\character::load( game[ "foley" ] ); + +*/ + +get_random_character( amount ) +{ + if (IsDefined( self.classname )) + self_info = strtok( self.classname, "_" ); + else + self_info = []; + if ( !common_scripts\utility::isSP() ) + { + if ( isDefined( self.pers["modelIndex"] ) && self.pers["modelIndex"] < amount ) + return self.pers["modelIndex"]; + + index = randomInt( amount ); + self.pers["modelIndex"] = index; + + return index; + } + else if ( self_info.size <= 2 ) + { + // some custom guy that doesn't use standard naming convention + return randomint( amount ); + } + + group = "auto"; // by default the type is an auto-selected character + index = undefined; + prefix = self_info[ 2 ]; // merc, marine, etc + + // the designer can overwrite the character + if ( isdefined( self.script_char_index ) ) + { + index = self.script_char_index; + } + + // the designer can hint that this guy is a member of a group of like - spawned guys, so he should use a different index + if ( isdefined( self.script_char_group ) ) + { + type = "grouped"; + group = "group_" + self.script_char_group; + } + + if ( !isdefined( level.character_index_cache ) ) + { + // separately store script grouped guys and auto guys so that they dont influence each other + level.character_index_cache = []; + } + + if ( !isdefined( level.character_index_cache[ prefix ] ) ) + { + // separately store script grouped guys and auto guys so that they dont influence each other + level.character_index_cache[ prefix ] = []; + } + + if ( !isdefined( level.character_index_cache[ prefix ][ group ] ) ) + { + initialize_character_group( prefix, group, amount ); + } + + if ( !isdefined( index ) ) + { + index = get_least_used_index( prefix, group ); + + if ( !isdefined( index ) ) + { + // fail safe + index = randomint( 5000 ); + } + } + + + while ( index >= amount ) + { + index -= amount; + } + + level.character_index_cache[ prefix ][ group ][ index ]++; + + return index; +} + +get_least_used_index( prefix, group ) +{ + lowest_indices = []; + lowest_use = level.character_index_cache[ prefix ][ group ][ 0 ]; + lowest_indices[ 0 ] = 0; + + for ( i = 1; i < level.character_index_cache[ prefix ][ group ].size; i++ ) + { + if ( level.character_index_cache[ prefix ][ group ][ i ] > lowest_use ) + { + continue; + } + + if ( level.character_index_cache[ prefix ][ group ][ i ] < lowest_use ) + { + // if its the new lowest, start over on the array + lowest_indices = []; + lowest_use = level.character_index_cache[ prefix ][ group ][ i ]; + } + + // the equal amounts end up in the array + lowest_indices[ lowest_indices.size ] = i; + } + assertex( lowest_indices.size, "Tried to spawn a character but the lowest indices didn't exist" ); + return random( lowest_indices ); +} + +initialize_character_group( prefix, group, amount ) +{ + for ( i = 0; i < amount; i++ ) + { + level.character_index_cache[ prefix ][ group ][ i ] = 0; + } +} + +get_random_weapon( amount ) +{ + return randomint( amount ); +} + +random( array ) +{ + return array [ randomint( array.size ) ]; +} \ No newline at end of file diff --git a/raw/codescripts/delete.gsc b/raw/codescripts/delete.gsc new file mode 100644 index 0000000..5303470 --- /dev/null +++ b/raw/codescripts/delete.gsc @@ -0,0 +1,7 @@ +main() +{ + assert(isdefined(self)); + wait 0; + if (isdefined(self)) + self delete(); +} diff --git a/raw/codescripts/struct.gsc b/raw/codescripts/struct.gsc new file mode 100644 index 0000000..f524456 --- /dev/null +++ b/raw/codescripts/struct.gsc @@ -0,0 +1,11 @@ +InitStructs() +{ + level.struct = []; +} + +CreateStruct() +{ + struct = spawnstruct(); + level.struct[level.struct.size] = struct; + return struct; +} diff --git a/raw/common_scripts/_artcommon.gsc b/raw/common_scripts/_artcommon.gsc new file mode 100644 index 0000000..34d7968 --- /dev/null +++ b/raw/common_scripts/_artcommon.gsc @@ -0,0 +1,547 @@ +#include common_scripts\utility; + +setfogsliders() +{ + /$ + // The read-only vars are set each time a call to SetExpFog is made, so they should contain the 'fog dest' params + SetDevDvar( "scr_fog_exp_halfplane", GetDvar( "g_fogHalfDistReadOnly", 0.0 ) ); + SetDevDvar( "scr_fog_nearplane", GetDvar( "g_fogStartDistReadOnly", 0.1 ) ); + SetDevDvar( "scr_fog_color", GetDvar( "g_fogColorReadOnly", ( 1, 0, 0 ) ) ); + SetDevDvar( "scr_fog_color_intensity", GetDvar( "g_fogColorIntensityReadOnly", 1.0 ) ); + SetDevDvar( "scr_fog_max_opacity", GetDvar( "g_fogMaxOpacityReadOnly", 1.0 ) ); + SetDevDvar( "scr_sunFogEnabled", GetDvar( "g_sunFogEnabledReadOnly", 0 ) ); + SetDevDvar( "scr_sunFogColor", GetDvar( "g_sunFogColorReadOnly", ( 1, 0, 0 ) ) ); + SetDevDvar( "scr_sunfogColorIntensity", GetDvar( "g_sunFogColorIntensityReadOnly", 1.0 ) ); + SetDevDvar( "scr_sunFogDir", GetDvarVector( "g_sunFogDirReadOnly", ( 1, 0, 0 ) ) ); + SetDevDvar( "scr_sunFogBeginFadeAngle", GetDvar( "g_sunFogBeginFadeAngleReadOnly", 0.0 ) ); + SetDevDvar( "scr_sunFogEndFadeAngle", GetDvar( "g_sunFogEndFadeAngleReadOnly", 180.0 ) ); + SetDevDvar( "scr_sunFogScale", GetDvar( "g_sunFogScaleReadOnly", 1.0 ) ); + SetDevDvar( "scr_heightFogEnabled", GetDvar( "g_heightFogEnabledReadOnly", 0 ) ); + SetDevDvar( "scr_heightFogBaseHeight", GetDvar( "g_heightFogBaseHeightReadOnly", 0 ) ); + SetDevDvar( "scr_heightFogHalfPlaneDistance", GetDvar( "g_heightFogHalfPlaneDistanceReadOnly", 1000 ) ); + + // The r_sky_fog vars are only active if tweaks on them are enabled, which is a little strange... + SetDevDvar( "scr_skyFogIntensity", GetDvar( "r_sky_fog_intensity", 0.0 ) ); + SetDevDvar( "scr_skyFogMinAngle", GetDvar( "r_sky_fog_min_angle", 0.0 ) ); + SetDevDvar( "scr_skyFogMaxAngle", GetDvar( "r_sky_fog_max_angle", 90.0 ) ); + + SetDevDvar( "scr_atmosFogEnabled", GetDvar( "g_atmosFogEnabledReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogSunFogColor", GetDvar( "g_atmosFogSunFogColorReadOnly", (.5, .5, .5 ) ) ); + SetDevDvar( "scr_atmosFogHazeColor", GetDvar( "g_atmosFogHazeColorReadOnly", (.5, .5, .5 ) ) ); + SetDevDvar( "scr_atmosFogHazeStrength", GetDvar( "g_atmosFogHazeStrengthReadOnly", .5 ) ); + SetDevDvar( "scr_atmosFogHazeSpread", GetDvar( "g_atmosFogHazeSpreadReadOnly", .75 ) ); + SetDevDvar( "scr_atmosFogExtinctionStrength", GetDvar( "g_atmosFogExtinctionStrengthReadOnly", 1 ) ); + SetDevDvar( "scr_atmosFogInScatterStrength", GetDvar( "g_atmosFogInScatterStrengthReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogHalfPlaneDistance", GetDvar( "g_atmosFogHalfPlaneDistanceReadOnly", 5000 ) ); + SetDevDvar( "scr_atmosFogStartDistance", GetDvar( "g_atmosFogStartDistanceReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogDistanceScale", GetDvar( "g_atmosFogDistanceScaleReadOnly", 1 ) ); + SetDevDvar( "scr_atmosFogSkyDistance", int( GetDvar( "g_atmosFogSkyDistanceReadOnly", 100000 ) ) ); + SetDevDvar( "scr_atmosFogSkyAngularFalloffEnabled", GetDvar( "g_atmosFogSkyAngularFalloffEnabledReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogSkyFalloffStartAngle", GetDvar( "g_atmosFogSkyFalloffStartAngleReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogSkyFalloffAngleRange", GetDvar( "g_atmosFogSkyFalloffAngleRangeReadOnly", 90 ) ); + SetDevDvar( "scr_atmosFogSunDirection", GetDvarVector( "g_atmosFogSunDirectionReadOnly", (0, 0, 1) ) ); + SetDevDvar( "scr_atmosFogHeightFogEnabled", GetDvar( "g_atmosFogHeightFogEnabledReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogHeightFogBaseHeight", GetDvar( "g_atmosFogHeightFogBaseHeightReadOnly", 0 ) ); + SetDevDvar( "scr_atmosFogHeightFogHalfPlaneDistance", GetDvar( "g_atmosFogHeightFogHalfPlaneDistanceReadOnly", 1000 ) ); + $/ +} + +/$ +translateFogSlidersToScript() +{ + level.fogexphalfplane = limit( GetDvarFloat( "scr_fog_exp_halfplane" ) ); + level.fognearplane = limit( GetDvarFloat( "scr_fog_nearplane" ) ); + level.fogHDRColorIntensity = limit( GetDvarFloat( "scr_fog_color_intensity" ) ); + level.fogmaxopacity = limit( GetDvarFloat( "scr_fog_max_opacity" ) ); + level.sunFogEnabled = GetDvarInt( "scr_sunFogEnabled" ); + level.sunFogHDRColorIntensity = limit( GetDvarFloat( "scr_sunFogColorIntensity" ) ); + level.sunFogBeginFadeAngle = limit( GetDvarFloat( "scr_sunFogBeginFadeAngle" ) ); + level.sunFogEndFadeAngle = limit( GetDvarFloat( "scr_sunFogEndFadeAngle" ) ); + level.sunFogScale = limit( GetDvarFloat( "scr_sunFogScale" ) ); + level.skyFogIntensity = limit( GetDvarFloat( "scr_skyFogIntensity" ) ); + level.skyFogMinAngle = limit( GetDvarFloat( "scr_skyFogMinAngle" ) ); + level.skyFogMaxAngle = limit( GetDvarFloat( "scr_skyFogMaxAngle" ) ); + level.heightFogEnabled = GetDvarInt( "scr_heightFogEnabled" ); + level.heightFogBaseHeight = limit( GetDvarFloat( "scr_heightFogBaseHeight" ) ); + level.heightFogHalfPlaneDistance = limit( GetDvarFloat( "scr_heightFogHalfPlaneDistance" ) ); + + fogColor = GetDvarVector( "scr_fog_color" ); + r = limit( fogColor[0] ); + g = limit( fogColor[1] ); + b = limit( fogColor[2] ); + level.fogcolor = ( r, g , b ); + + sunFogColor = GetDvarVector( "scr_sunFogColor" ); + r = limit( sunFogColor[0] ); + g = limit( sunFogColor[1] ); + b = limit( sunFogColor[2] ); + level.sunFogColor =( r, g , b ); + + sunFogDir = GetDvarVector( "scr_sunFogDir" ); + x = limit( sunFogDir[0]); + y = limit( sunFogDir[1]); + z = limit( sunFogDir[2]); + level.sunFogDir = (x,y,z); + + level.atmosFogEnabled = GetDvarInt( "scr_atmosFogEnabled" ); + vec3 = GetDvarVector( "scr_atmosFogSunFogColor" ); + x = limit( vec3[0] ); + y = limit( vec3[1] ); + z = limit( vec3[2] ); + level.atmosFogSunFogColor = ( x, y, z ); + vec3 = GetDvarVector( "scr_atmosFogHazeColor" ); + x = limit( vec3[0] ); + y = limit( vec3[1] ); + z = limit( vec3[2] ); + level.atmosFogHazeColor = ( x, y, z ); + level.atmosFogHazeStrength = limit( GetDvarFloat( "scr_atmosFogHazeStrength" ) ); + level.atmosFogHazeSpread = limit( GetDvarFloat( "scr_atmosFogHazeSpread" ) ); + level.atmosFogExtinctionStrength = limit( GetDvarFloat( "scr_atmosFogExtinctionStrength" ) ); + level.atmosFogInScatterStrength = limit( GetDvarFloat( "scr_atmosFogInScatterStrength" ) ); + level.atmosFogHalfPlaneDistance = limit( GetDvarFloat( "scr_atmosFogHalfPlaneDistance" ) ); + level.atmosFogStartDistance = limit( GetDvarFloat( "scr_atmosFogStartDistance" ) ); + level.atmosFogDistanceScale = limit( GetDvarFloat( "scr_atmosFogDistanceScale" ) ); + level.atmosFogSkyDistance = GetDvarInt( "scr_atmosFogSkyDistance" ); + level.atmosFogSkyAngularFalloffEnabled = GetDvarInt( "scr_atmosFogSkyAngularFalloffEnabled" ); + level.atmosFogSkyFalloffStartAngle = limit( GetDvarFloat( "scr_atmosFogSkyFalloffStartAngle" ) ); + level.atmosFogSkyFalloffAngleRange = limit( GetDvarFloat( "scr_atmosFogSkyFalloffAngleRange" ) ); + vec3 = GetDvarVector( "scr_atmosFogSunDirection" ); + x = limit( vec3[0] ); + y = limit( vec3[1] ); + z = limit( vec3[2] ); + level.atmosFogSunDirection = ( x, y, z ); + level.atmosFogHeightFogEnabled = GetDvarInt( "scr_atmosFogHeightFogEnabled" ); + level.atmosFogHeightFogBaseHeight = limit( GetDvarFloat( "scr_atmosFogHeightFogBaseHeight" ) ); + level.atmosFogHeightFogHalfPlaneDistance = limit( GetDvarFloat( "scr_atmosFogHeightFogHalfPlaneDistance" ) ); +} + +limit( i) +{ + limit = 0.001; + if ( ( i < limit ) && ( i > ( limit * -1 ) ) ) + i = 0; + return i; +} + +updateFogEntFromScript() +{ + if ( GetDvarInt( "scr_cmd_plr_sun" ) ) + { + SetDevDvar( "scr_sunFogDir", AnglesToForward( level.player GetPlayerAngles() ) ); + SetDevDvar( "scr_cmd_plr_sun", 0 ); + } + if ( GetDvarInt( "scr_cmd_plr_sun_atmos_fog" ) ) + { + SetDevDvar( "scr_atmosFogSunDirection", AnglesToForward( level.player GetPlayerAngles() ) ); + SetDevDvar( "scr_cmd_plr_sun_atmos_fog", 0 ); + } + vision_set_name = ToLower(level.vision_set_transition_ent.vision_set); + if ( level.currentgen ) + { + vision_set_name_cg = vision_set_name + "_cg"; + if ( IsDefined( level.vision_set_fog[ vision_set_name_cg ] ) ) + vision_set_name = vision_set_name_cg; + } + ent = level.vision_set_fog[ vision_set_name ]; + + if ( isdefined( ent ) && isdefined( ent.HDROverride ) && isdefined( level.vision_set_fog[ ToLower(ent.HDROverride) ] ) ) + { + ent = level.vision_set_fog[ ToLower(ent.HDROverride) ]; + } + + if( IsDefined( ent ) && isdefined( ent.name ) ) + { + ent.startDist = level.fognearplane; + ent.halfwayDist = level.fogexphalfplane; + ent.red = level.fogcolor[ 0 ]; + ent.green = level.fogcolor[ 1 ]; + ent.blue = level.fogcolor[ 2 ]; + ent.HDRColorIntensity = level.fogHDRColorIntensity; + ent.maxOpacity = level.fogmaxopacity; + ent.sunFogEnabled = level.sunFogEnabled; + ent.sunRed = level.sunFogColor[ 0 ]; + ent.sunGreen = level.sunFogColor[ 1 ]; + ent.sunBlue = level.sunFogColor[ 2 ]; + ent.HDRSunColorIntensity = level.sunFogHDRColorIntensity; + ent.sunDir = level.sunFogDir; + ent.sunBeginFadeAngle = level.sunFogBeginFadeAngle; + ent.sunEndFadeAngle = level.sunFogEndFadeAngle; + ent.normalFogScale = level.sunFogScale; + ent.skyFogIntensity = level.skyFogIntensity; + ent.skyFogMinAngle = level.skyFogMinAngle; + ent.skyFogMaxAngle = level.skyFogMaxAngle; + + if ( IsDefined( level.heightFogEnabled ) && IsDefined( level.heightFogBaseHeight ) && IsDefined( level.heightFogHalfPlaneDistance ) ) + { + ent.heightFogEnabled = level.heightFogEnabled; + ent.heightFogBaseHeight = level.heightFogBaseHeight; + ent.heightFogHalfPlaneDistance = level.heightFogHalfPlaneDistance; + } + else + { + ent.heightFogEnabled = 0; + ent.heightFogBaseHeight = 0; + ent.heightFogHalfPlaneDistance = 1000; + } + + if ( IsDefined( level.atmosFogEnabled ) ) + { + Assert( IsDefined( level.atmosFogSunFogColor ) ); + Assert( IsDefined( level.atmosFogHazeColor ) ); + Assert( IsDefined( level.atmosFogHazeStrength ) ); + Assert( IsDefined( level.atmosFogHazeSpread ) ); + Assert( IsDefined( level.atmosFogExtinctionStrength ) ); + Assert( IsDefined( level.atmosFogInScatterStrength ) ); + Assert( IsDefined( level.atmosFogHalfPlaneDistance ) ); + Assert( IsDefined( level.atmosFogStartDistance ) ); + Assert( IsDefined( level.atmosFogDistanceScale ) ); + Assert( IsDefined( level.atmosFogSkyDistance ) ); + Assert( IsDefined( level.atmosFogSkyAngularFalloffEnabled ) ); + Assert( IsDefined( level.atmosFogSkyFalloffStartAngle ) ); + Assert( IsDefined( level.atmosFogSkyFalloffAngleRange ) ); + Assert( IsDefined( level.atmosFogSunDirection ) ); + Assert( IsDefined( level.atmosFogHeightFogEnabled ) ); + Assert( IsDefined( level.atmosFogHeightFogBaseHeight ) ); + Assert( IsDefined( level.atmosFogHeightFogHalfPlaneDistance ) ); + + ent.atmosFogEnabled = level.atmosFogEnabled; + ent.atmosFogSunFogColor = level.atmosFogSunFogColor; + ent.atmosFogHazeColor = level.atmosFogHazeColor; + ent.atmosFogHazeStrength = level.atmosFogHazeStrength; + ent.atmosFogHazeSpread = level.atmosFogHazeSpread; + ent.atmosFogExtinctionStrength = level.atmosFogExtinctionStrength; + ent.atmosFogInScatterStrength = level.atmosFogInScatterStrength; + ent.atmosFogHalfPlaneDistance = level.atmosFogHalfPlaneDistance; + ent.atmosFogStartDistance = level.atmosFogStartDistance; + ent.atmosFogDistanceScale = level.atmosFogDistanceScale; + ent.atmosFogSkyDistance = level.atmosFogSkyDistance; + ent.atmosFogSkyAngularFalloffEnabled = level.atmosFogSkyAngularFalloffEnabled; + ent.atmosFogSkyFalloffStartAngle = level.atmosFogSkyFalloffStartAngle; + ent.atmosFogSkyFalloffAngleRange = level.atmosFogSkyFalloffAngleRange; + ent.atmosFogSunDirection = level.atmosFogSunDirection; + ent.atmosFogHeightFogEnabled = level.atmosFogHeightFogEnabled; + ent.atmosFogHeightFogBaseHeight = level.atmosFogHeightFogBaseHeight; + ent.atmosFogHeightFogHalfPlaneDistance = level.atmosFogHeightFogHalfPlaneDistance; + } + else + { + if ( IsDefined( ent.atmosFogEnabled ) ) + { + ent.atmosFogEnabled = 0; + } + } + + if ( GetDvarInt( "scr_fog_disable" ) ) + { + ent.startDist = 2000000000; + ent.halfwayDist = 2000000001; + ent.red = 0; + ent.green = 0; + ent.blue = 0; + ent.HDRColorIntensity = 1; + ent.HDRSunColorIntensity = 1; + ent.maxOpacity = 0; + ent.skyFogIntensity = 0; + ent.heightFogEnabled = 0; + ent.heightFogBaseHeight = 0; + ent.heightFogHalfPlaneDistance = 1000; + + if ( IsDefined( ent.atmosFogEnabled ) ) + { + ent.atmosFogEnabled = 0; + } + } + + set_fog_to_ent_values( ent, 0 ); + } +} + +fogslidercheck() +{ + // catch all those cases where a slider can be pushed to a place of conflict + if ( level.sunFogBeginFadeAngle >= level.sunFogEndFadeAngle ) + { + level.sunFogBeginFadeAngle = level.sunFogEndFadeAngle - 1; + SetDvar( "scr_sunFogBeginFadeAngle", level.sunFogBeginFadeAngle ); + } + + if ( level.sunFogEndFadeAngle <= level.sunFogBeginFadeAngle ) + { + level.sunFogEndFadeAngle = level.sunFogBeginFadeAngle + 1; + SetDvar( "scr_sunFogEndFadeAngle", level.sunFogEndFadeAngle ); + } +} + +add_vision_set_to_list( vision_set_name ) +{ + assert( IsDefined( level.vision_set_names ) ); + + found = array_find( level.vision_set_names, vision_set_name ); + if ( IsDefined( found ) ) + return; + + level.vision_set_names = array_add( level.vision_set_names, vision_set_name ); +} + +print_vision( vision_set ) +{ + found = array_find( level.vision_set_names, vision_set ); + if ( !IsDefined( found ) ) + return; + + fileprint_launcher_start_file(); + + // Glow + fileprint_launcher( "r_glow \"" + GetDvar( "r_glowTweakEnable" ) + "\"" ); + fileprint_launcher( "r_glowRadius0 \"" + GetDvar( "r_glowTweakRadius0" ) + "\"" ); + fileprint_launcher( "r_glowBloomPinch \"" + GetDvar( "r_glowTweakBloomPinch" ) + "\"" ); + fileprint_launcher( "r_glowBloomCutoff \"" + GetDvar( "r_glowTweakBloomCutoff" ) + "\"" ); + fileprint_launcher( "r_glowBloomDesaturation \"" + GetDvar( "r_glowTweakBloomDesaturation" ) + "\"" ); + fileprint_launcher( "r_glowBloomIntensity0 \"" + GetDvar( "r_glowTweakBloomIntensity0" ) + "\"" ); + fileprint_launcher( "r_glowUseAltCutoff \"" + GetDvar( "r_glowTweakUseAltCutoff" ) + "\"" ); + fileprint_launcher( "" ); + + // Film + fileprint_launcher( "r_filmEnable \"" + GetDvar( "r_filmTweakEnable" ) + "\"" ); + fileprint_launcher( "r_filmContrast \"" + GetDvar( "r_filmTweakContrast" ) + "\"" ); + if( level.currentgen ) + fileprint_launcher( "r_filmIntensity \"" + GetDvar( "r_filmTweakIntensity" ) + "\"" ); + fileprint_launcher( "r_filmBrightness \"" + GetDvar( "r_filmTweakBrightness" ) + "\"" ); + fileprint_launcher( "r_filmDesaturation \"" + GetDvar( "r_filmTweakDesaturation" ) + "\"" ); + fileprint_launcher( "r_filmDesaturationDark \"" + GetDvar( "r_filmTweakDesaturationDark" ) + "\"" ); + fileprint_launcher( "r_filmInvert \"" + GetDvar( "r_filmTweakInvert" ) + "\"" ); + fileprint_launcher( "r_filmLightTint \"" + GetDvar( "r_filmTweakLightTint" ) + "\"" ); + fileprint_launcher( "r_filmMediumTint \"" + GetDvar( "r_filmTweakMediumTint" ) + "\"" ); + fileprint_launcher( "r_filmDarkTint \"" + GetDvar( "r_filmTweakDarkTint" ) + "\"" ); + fileprint_launcher( " " ); + + // Character Light + fileprint_launcher( "r_primaryLightUseTweaks \"" + GetDvar( "r_primaryLightUseTweaks" ) + "\"" ); + fileprint_launcher( "r_primaryLightTweakDiffuseStrength \"" + GetDvar( "r_primaryLightTweakDiffuseStrength" ) + "\"" ); + fileprint_launcher( "r_primaryLightTweakSpecularStrength \"" + GetDvar( "r_primaryLightTweakSpecularStrength" ) + "\"" ); + fileprint_launcher( "r_charLightAmbient \"" + GetDvar( "r_charLightAmbient" ) + "\"" ); + fileprint_launcher( " " ); + + // Viewmodel Light + fileprint_launcher( "r_viewModelPrimaryLightUseTweaks \"" + GetDvar( "r_viewModelPrimaryLightUseTweaks" ) + "\"" ); + fileprint_launcher( "r_viewModelPrimaryLightTweakDiffuseStrength \"" + GetDvar( "r_viewModelPrimaryLightTweakDiffuseStrength" ) + "\"" ); + fileprint_launcher( "r_viewModelPrimaryLightTweakSpecularStrength \"" + GetDvar( "r_viewModelPrimaryLightTweakSpecularStrength" ) + "\"" ); + fileprint_launcher( "r_viewModelLightAmbient \"" + GetDvar( "r_viewModelLightAmbient" ) + "\"" ); + fileprint_launcher( " " ); + + // Volume Light Scatter + fileprint_launcher( "r_volumeLightScatter \"" + GetDvar( "r_volumeLightScatterUseTweaks" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterLinearAtten \"" + GetDvar( "r_volumeLightScatterLinearAtten" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterQuadraticAtten \"" + GetDvar( "r_volumeLightScatterQuadraticAtten" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterAngularAtten \"" + GetDvar( "r_volumeLightScatterAngularAtten" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterDepthAttenNear \"" + GetDvar( "r_volumeLightScatterDepthAttenNear" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterDepthAttenFar \"" + GetDvar( "r_volumeLightScatterDepthAttenFar" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterBackgroundDistance \"" + GetDvar( "r_volumeLightScatterBackgroundDistance" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterColor \"" + GetDvar( "r_volumeLightScatterColor" ) + "\"" ); + fileprint_launcher( "r_volumeLightScatterEv \"" + GetDvar( "r_volumeLightScatterEv" ) + "\"" ); + fileprint_launcher( " " ); + + // Rim Light (keep in sync with #define RIM_LIGHTING in platform_defines.h) + fileprint_launcher( "r_rimLightUseTweaks \"" + GetDvar( "r_rimLightUseTweaks" ) + "\"" ); + fileprint_launcher( "r_rimLight0Pitch \"" + GetDvar( "r_rimLight0Pitch" ) + "\"" ); + fileprint_launcher( "r_rimLight0Heading \"" + GetDvar( "r_rimLight0Heading" ) + "\"" ); + fileprint_launcher( "r_rimLightDiffuseIntensity \"" + GetDvar( "r_rimLightDiffuseIntensity" ) + "\"" ); + fileprint_launcher( "r_rimLightSpecIntensity \"" + GetDvar( "r_rimLightSpecIntensity" ) + "\"" ); + fileprint_launcher( "r_rimLightBias \"" + GetDvar( "r_rimLightBias" ) + "\"" ); + fileprint_launcher( "r_rimLightPower \"" + GetDvar( "r_rimLightPower" ) + "\"" ); + fileprint_launcher( "r_rimLight0Color \"" + GetDvar( "r_rimLight0Color" ) + "\"" ); + fileprint_launcher( "r_rimLightFalloffMaxDistance \"" + GetDvar( "r_rimLightFalloffMaxDistance" ) + "\"" ); + fileprint_launcher( "r_rimLightFalloffMinDistance \"" + GetDvar( "r_rimLightFalloffMinDistance" ) + "\"" ); + fileprint_launcher( "r_rimLightFalloffMinIntensity \"" + GetDvar( "r_rimLightFalloffMinIntensity" ) + "\"" ); + fileprint_launcher( " " ); + + // Unlit Surface + fileprint_launcher( "r_unlitSurfaceHDRScalar \"" + GetDvar( "r_unlitSurfaceHDRScalar" ) + "\"" ); + fileprint_launcher( "" ); + + // Chromatic Aberration + fileprint_launcher( "r_chromaticAberrationMode \"" + GetDvar( "r_chromaticAberration" ) + "\"" ); + fileprint_launcher( "r_chromaticSeparation \"" + GetDvar( "r_chromaticSeparationR" ) + " " + GetDvar( "r_chromaticSeparationG" ) + " " + GetDvar( "r_chromaticSeparationB" ) + "\"" ); + fileprint_launcher( "r_chromaticAberrationAlpha \"" + GetDvar( "r_chromaticAberrationAlpha" ) + "\"" ); + + visionFileName = "\\share\\raw\\vision\\" + vision_set + ".vision"; + + return fileprint_launcher_end_file( visionFileName, true ); +} + +get_lightset_filename() +{ + if ( level.nextgen ) + { + // [nextgen-begin] + return "\\share\\raw\\maps\\createart\\" + get_template_level() + "_lightsets_hdr.csv"; + // [nextgen-end] + } + else + { + return "\\share\\raw\\maps\\createart\\" + get_template_level() + "_lightsets.csv"; + } +} + +print_lightset( lightset_filename ) +{ + fileprint_launcher_start_file(); + + PrintLightSetSettings(); + + return fileprint_launcher_end_file( lightset_filename, true ); + +} + + +print_fog_ents( forMP ) +{ + foreach( ent in level.vision_set_fog ) + { + if( !isdefined( ent.name ) ) + continue; + + ConvertLegacyFog( ent ); + + if ( forMP ) + fileprint_launcher( "\tent = maps\\mp\\_art::create_vision_set_fog( \"" + ent.name + "\" );"); + else + fileprint_launcher( "\tent = maps\\_utility::create_vision_set_fog( \"" + ent.name + "\" );"); + + if( isdefined( ent.startDist ) ) + fileprint_launcher( "\tent.startDist = "+ent.startDist + ";" ); + if( isdefined( ent.halfwayDist ) ) + fileprint_launcher( "\tent.halfwayDist = "+ent.halfwayDist + ";" ); + if( isdefined( ent.red ) ) + fileprint_launcher( "\tent.red = "+ent.red + ";" ); + if( isdefined( ent.green ) ) + fileprint_launcher( "\tent.green = "+ent.green + ";" ); + if( isdefined( ent.blue ) ) + fileprint_launcher( "\tent.blue = "+ent.blue + ";" ); + if( isdefined( ent.HDRColorIntensity ) ) + fileprint_launcher( "\tent.HDRColorIntensity = "+ent.HDRColorIntensity + ";" ); + if( isdefined( ent.maxOpacity ) ) + fileprint_launcher( "\tent.maxOpacity = "+ent.maxOpacity + ";" ); + if( isdefined( ent.transitionTime ) ) + fileprint_launcher( "\tent.transitionTime = "+ent.transitionTime + ";" ); + if( isdefined( ent.sunFogEnabled ) ) + fileprint_launcher( "\tent.sunFogEnabled = "+ent.sunFogEnabled + ";" ); + if( isdefined( ent.sunRed ) ) + fileprint_launcher( "\tent.sunRed = "+ent.sunRed + ";" ); + if( isdefined( ent.sunGreen ) ) + fileprint_launcher( "\tent.sunGreen = "+ent.sunGreen + ";" ); + if( isdefined( ent.sunBlue ) ) + fileprint_launcher( "\tent.sunBlue = "+ent.sunBlue + ";" ); + if( isdefined( ent.HDRSunColorIntensity ) ) + fileprint_launcher( "\tent.HDRSunColorIntensity = "+ent.HDRSunColorIntensity + ";" ); + if( isdefined( ent.sunDir ) ) + fileprint_launcher( "\tent.sunDir = "+ent.sunDir + ";" ); + if( isdefined( ent.sunBeginFadeAngle ) ) + fileprint_launcher( "\tent.sunBeginFadeAngle = "+ent.sunBeginFadeAngle + ";" ); + if( isdefined( ent.sunEndFadeAngle ) ) + fileprint_launcher( "\tent.sunEndFadeAngle = "+ent.sunEndFadeAngle + ";" ); + if( isdefined( ent.normalFogScale ) ) + fileprint_launcher( "\tent.normalFogScale = "+ent.normalFogScale + ";" ); + if( isdefined( ent.skyFogIntensity ) ) + fileprint_launcher( "\tent.skyFogIntensity = "+ent.skyFogIntensity + ";" ); + if( isdefined( ent.skyFogMinAngle ) ) + fileprint_launcher( "\tent.skyFogMinAngle = "+ent.skyFogMinAngle + ";" ); + if( isdefined( ent.skyFogMaxAngle ) ) + fileprint_launcher( "\tent.skyFogMaxAngle = "+ent.skyFogMaxAngle + ";" ); + if ( IsDefined( ent.HDROverride ) ) + fileprint_launcher( "\tent.HDROverride = \"" + ent.HDROverride + "\";" ); + if ( IsDefined( ent.heightFogEnabled ) ) + fileprint_launcher( "\tent.heightFogEnabled = " + ent.heightFogEnabled + ";" ); + if ( IsDefined( ent.heightFogBaseHeight ) ) + fileprint_launcher( "\tent.heightFogBaseHeight = " + ent.heightFogBaseHeight + ";" ); + if ( IsDefined( ent.heightFogHalfPlaneDistance ) ) + fileprint_launcher( "\tent.heightFogHalfPlaneDistance = " + ent.heightFogHalfPlaneDistance + ";" ); + + if ( IsDefined( ent.atmosFogEnabled ) ) + fileprint_launcher( "\tent.atmosFogEnabled = " + ent.atmosFogEnabled + ";" ); + if ( IsDefined( ent.atmosFogSunFogColor ) ) + fileprint_launcher( "\tent.atmosFogSunFogColor = " + ent.atmosFogSunFogColor + ";" ); + if ( IsDefined( ent.atmosFogHazeColor ) ) + fileprint_launcher( "\tent.atmosFogHazeColor = " + ent.atmosFogHazeColor + ";" ); + if ( IsDefined( ent.atmosFogHazeStrength ) ) + fileprint_launcher( "\tent.atmosFogHazeStrength = " + ent.atmosFogHazeStrength + ";" ); + if ( IsDefined( ent.atmosFogHazeSpread ) ) + fileprint_launcher( "\tent.atmosFogHazeSpread = " + ent.atmosFogHazeSpread + ";" ); + if ( IsDefined( ent.atmosFogExtinctionStrength ) ) + fileprint_launcher( "\tent.atmosFogExtinctionStrength = " + ent.atmosFogExtinctionStrength + ";" ); + if ( IsDefined( ent.atmosFogInScatterStrength ) ) + fileprint_launcher( "\tent.atmosFogInScatterStrength = " + ent.atmosFogInScatterStrength + ";" ); + if ( IsDefined( ent.atmosFogHalfPlaneDistance ) ) + fileprint_launcher( "\tent.atmosFogHalfPlaneDistance = " + ent.atmosFogHalfPlaneDistance + ";" ); + if ( IsDefined( ent.atmosFogStartDistance ) ) + fileprint_launcher( "\tent.atmosFogStartDistance = " + ent.atmosFogStartDistance + ";" ); + if ( IsDefined( ent.atmosFogDistanceScale ) ) + fileprint_launcher( "\tent.atmosFogDistanceScale = " + ent.atmosFogDistanceScale + ";" ); + if ( IsDefined( ent.atmosFogSkyDistance ) ) + fileprint_launcher( "\tent.atmosFogSkyDistance = " + int( ent.atmosFogSkyDistance ) + ";" ); + if ( IsDefined( ent.atmosFogSkyAngularFalloffEnabled ) ) + fileprint_launcher( "\tent.atmosFogSkyAngularFalloffEnabled = " + ent.atmosFogSkyAngularFalloffEnabled + ";" ); + if ( IsDefined( ent.atmosFogSkyFalloffStartAngle ) ) + fileprint_launcher( "\tent.atmosFogSkyFalloffStartAngle = " + ent.atmosFogSkyFalloffStartAngle + ";" ); + if ( IsDefined( ent.atmosFogSkyFalloffAngleRange ) ) + fileprint_launcher( "\tent.atmosFogSkyFalloffAngleRange = " + ent.atmosFogSkyFalloffAngleRange + ";" ); + if ( IsDefined( ent.atmosFogSunDirection ) ) + fileprint_launcher( "\tent.atmosFogSunDirection = " + ent.atmosFogSunDirection + ";" ); + if ( IsDefined( ent.atmosFogHeightFogEnabled ) ) + fileprint_launcher( "\tent.atmosFogHeightFogEnabled = " + ent.atmosFogHeightFogEnabled + ";" ); + if ( IsDefined( ent.atmosFogHeightFogBaseHeight ) ) + fileprint_launcher( "\tent.atmosFogHeightFogBaseHeight = " + ent.atmosFogHeightFogBaseHeight + ";" ); + if ( IsDefined( ent.atmosFogHeightFogHalfPlaneDistance ) ) + fileprint_launcher( "\tent.atmosFogHeightFogHalfPlaneDistance = " + ent.atmosFogHeightFogHalfPlaneDistance + ";" ); + + if( isDefined( ent.stagedVisionSets ) ) + { + string = " "; + for( i = 0; i < ent.stagedVisionSets.size; i++ ) + { + string = string + "\""+ ent.stagedVisionSets[i] + "\""; + if ( i < ent.stagedVisionSets.size - 1 ) + string = string + ","; + string = string + " "; + } + + fileprint_launcher( "\tent.stagedVisionSets = [" + string + "];" ); + } + + fileprint_launcher ( " " ); + } + + if ( !forMP ) + { // MP holds this script in the setup_fog() function. + // put out our dev only call to our fog file (normally code will parse it and apply it clientside) + fileprint_launcher( "\t/$" ); + if ( IsUsingHDR() ) + fileprint_launcher( "\tlevel._art_fog_setup = maps\\createart\\" + level.script + "_fog_hdr::main;" ); + else + fileprint_launcher( "\tlevel._art_fog_setup = maps\\createart\\" + level.script + "_fog::main;" ); + fileprint_launcher( "\t$/" ); + } +} + +print_fog_ents_csv() +{ + foreach( ent in level.vision_set_fog ) + { + if( !isdefined( ent.name ) ) + continue; + + targettedByHDROverride = false; + foreach( ent2 in level.vision_set_fog ) + { + if ( isdefined(ent2.HDROverride) && ent2.HDROverride == ent.name ) + { + targettedByHDROverride = true; + break; + } + } + + if ( !targettedByHDROverride ) + fileprint_launcher( "rawfile,vision/"+ent.name+".vision"); + } +} +$/ diff --git a/raw/common_scripts/_bcs_location_trigs.gsc b/raw/common_scripts/_bcs_location_trigs.gsc new file mode 100644 index 0000000..eeb00dd --- /dev/null +++ b/raw/common_scripts/_bcs_location_trigs.gsc @@ -0,0 +1,3782 @@ +#include common_scripts\utility; + +bcs_location_trigs_init() +{ + // create array if it hasn't been done by the dds system from _dds::dds_init() + // having it created in both places is temporarily. I plan to remove the mw3 battlechatter later + if (IsDefined(level.dds) && IsDefined(anim.bcs_locations)) + return; + + ASSERT( !IsDefined( level.bcs_location_mappings ) ); + level.bcs_location_mappings = []; + + bcs_location_trigger_mapping(); + bcs_trigs_assign_aliases(); + + // now that the trigger ents have their aliases set on them, clear out our big array + // so we can save on script variables + level.bcs_location_mappings = undefined; + + anim.locationLastCalloutTimes = []; +} + +bcs_trigs_assign_aliases() +{ + ASSERT( !IsDefined( anim.bcs_locations ) ); + anim.bcs_locations = []; + + ents = GetEntArray(); + trigs = []; + foreach( trig in ents ) + { + if( IsDefined( trig.classname ) && IsSubStr( trig.classname, "trigger_multiple_bcs" ) ) + { + trigs[ trigs.size ] = trig; + } + } + + foreach( trig in trigs ) + { + if ( !IsDefined( level.bcs_location_mappings[ trig.classname ] ) ) + { + /# + // iPrintln( "^2" + "WARNING: Couldn't find bcs location mapping for battlechatter trigger with classname " + trig.classname ); + // do nothing since too many prints kills the command buffer + #/ + } + else + { + aliases = ParseLocationAliases( level.bcs_location_mappings[ trig.classname ] ); + if( aliases.size > 1 ) + { + aliases = array_randomize( aliases ); + } + + trig.locationAliases = aliases; + } + } + + anim.bcs_locations = trigs; +} + +// parses locationStr using a space as a token and returns an array of the data in that field +ParseLocationAliases( locationStr ) +{ + locationAliases = StrTok( locationStr, " " ); + return locationAliases; +} + +add_bcs_location_mapping( classname, alias ) +{ + // see if we have to add to an existing entry + if( IsDefined( level.bcs_location_mappings[ classname ] ) ) + { + existing = level.bcs_location_mappings[ classname ]; + existingArr = ParseLocationAliases( existing ); + aliases = ParseLocationAliases( alias ); + + foreach( a in aliases ) + { + foreach( e in existingArr ) + { + if( a == e ) + { + return; + } + } + } + + existing += " " + alias; + level.bcs_location_mappings[ classname ] = existing; + + return; + } + + // otherwise make a new entry + level.bcs_location_mappings[ classname ] = alias; +} + + +// here's where we set up each kind of trigger and map them to their (partial) soundaliases +bcs_location_trigger_mapping() +{ + if ( isSP() ) + { + generic_locations(); + + fusion_locations(); + sanfran_locations(); + sanfran_b_locations(); + greece_locations(); + seoul_locations(); + detroit_locations(); + betrayal_locations(); + lab_locations(); + recovery_locations(); + lagos_locations(); + + merida(); + + old_locations(); + } + else + { + laser2_mp(); + lab2_mp(); + mp_refraction(); + mp_prison(); + mp_dam(); + mp_detroit(); + mp_greenband(); + mp_instinct(); + mp_levity(); + mp_recovery(); + mp_solar(); + mp_terrace(); + mp_torqued(); + mp_venus(); + mp_comeback(); + old_locations_mp(); + } +} + +//--------------------------------------------------------- +// GENERICS +//--------------------------------------------------------- +generic_locations() +{ +/*QUAKED trigger_multiple_bcs_generic_doorway_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="doorway_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_doorway_generic", "doorway_generic" ); + +/*QUAKED trigger_multiple_bcs_generic_window_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="window_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_window_generic", "window_generic" ); + +/*QUAKED trigger_multiple_bcs_generic_1stfloor_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="1stfloor_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_1stfloor_generic", "1stfloor_generic" ); + +/*QUAKED trigger_multiple_bcs_generic_1stfloor_doorway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="1stfloor_doorway" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_1stfloor_doorway", "1stfloor_doorway" ); + +/*QUAKED trigger_multiple_bcs_generic_1stfloor_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="1stfloor_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_1stfloor_window", "1stfloor_window" ); + +/*QUAKED trigger_multiple_bcs_generic_2ndfloor_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="2ndfloor_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_2ndfloor_generic", "2ndfloor_generic" ); + +/*QUAKED trigger_multiple_bcs_generic_2ndfloor_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="2ndfloor_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_2ndfloor_window", "2ndfloor_window" ); + +/*QUAKED trigger_multiple_bcs_generic_rooftop (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rooftop" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_rooftop", "rooftop" ); + +/*QUAKED trigger_multiple_bcs_generic_2ndfloor_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="2ndfloor_balcony" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_generic_2ndfloor_balcony", "2ndfloor_balcony" ); +} + +//------------------------------// +//-------XSLICE BCS Trigs-------// +//------------------------------// + +fusion_locations() +{ +/*QUAKED trigger_multiple_bcs_fus_truck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_truck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_truck", "fus_truck" ); + +/*QUAKED trigger_multiple_bcs_fus_tower (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_tower" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_tower", "fus_tower" ); + +/*QUAKED trigger_multiple_bcs_fus_generator (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_generator" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_generator", "fus_generator" ); + +/*QUAKED trigger_multiple_bcs_fus_mt (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_mt" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_mt", "fus_mt" ); + +/*QUAKED trigger_multiple_bcs_fus_titan (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_titan" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_titan", "fus_titan" ); + +/*QUAKED trigger_multiple_bcs_fus_hill (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_hill" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_hill", "fus_hill" ); + +/*QUAKED trigger_multiple_bcs_fus_garage (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_garage" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_garage", "fus_garage" ); + +/*QUAKED trigger_multiple_bcs_fus_crane (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_crane" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_crane", "fus_crane" ); + +/*QUAKED trigger_multiple_bcs_fus_forklift (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_forklift" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_forklift", "fus_forklift" ); + +/*QUAKED trigger_multiple_bcs_fus_pillar (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_pillar" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_pillar", "fus_pillar" ); + +/*QUAKED trigger_multiple_bcs_fus_pipes (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_pipes" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_pipes", "fus_pipes" ); + +/*QUAKED trigger_multiple_bcs_fus_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_balcony" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_balcony", "fus_balcony" ); + +/*QUAKED trigger_multiple_bcs_fus_catwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_catwalk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_catwalk", "fus_catwalk" ); + +/*QUAKED trigger_multiple_bcs_fus_fueltrucks (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_fueltrucks" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_fueltrucks", "fus_fueltrucks" ); + +/*QUAKED trigger_multiple_bcs_fus_walkway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_walkway" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_walkway", "fus_walkway" ); + +/*QUAKED trigger_multiple_bcs_fus_stairs (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fus_stairs" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_fus_stairs", "fus_stairs" ); +} + +sanfran_locations() +{ + +/*QUAKED trigger_multiple_bcs_sfa_bus (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_bus" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_bus", "sfa_bus" ); + +/*QUAKED trigger_multiple_bcs_sfa_cardoor (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_cardoor" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_cardoor", "sfa_cardoor" ); + +/*QUAKED trigger_multiple_bcs_sfa_cargovan (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_cargovan" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_cargovan", "sfa_cargovan" ); + +/*QUAKED trigger_multiple_bcs_sfa_policecar (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_policecar" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_policecar", "sfa_policecar" ); + +/*QUAKED trigger_multiple_bcs_sfa_roadsign (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_roadsign" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_roadsign", "sfa_roadsign" ); + +/*QUAKED trigger_multiple_bcs_sfa_sidewalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_sidewalk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_sidewalk", "sfa_sidewalk" ); + +/*QUAKED trigger_multiple_bcs_sfa_sportscar (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_sportscar" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_sportscar", "sfa_sportscar" ); + +/*QUAKED trigger_multiple_bcs_sfa_topbus (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_topbus" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_topbus", "sfa_topbus" ); + +/*QUAKED trigger_multiple_bcs_sfa_tower (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_tower" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_tower", "sfa_tower" ); + +/*QUAKED trigger_multiple_bcs_sfa_trailer (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfa_trailer" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfa_trailer", "sfa_trailer" ); + + /*QUAKED trigger_multiple_bcs_sfa_truck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="sfa_truck" +*/ + add_bcs_location_mapping ("trigger_multiple_bcs_sfa_truck", "sfa_truck"); + +} + +sanfran_b_locations() +{ + +/*QUAKED trigger_multiple_bcs_sfb_above (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="above" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_above", "above" ); + +/*QUAKED trigger_multiple_bcs_sfb_containers (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_containers" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_containers", "sfb_containers" ); + +/*QUAKED trigger_multiple_bcs_sfb_doors (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="doors" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_doors", "doors" ); + +/*QUAKED trigger_multiple_bcs_sfb_helipad (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="helipad" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_helipad", "helipad" ); + +/*QUAKED trigger_multiple_bcs_sfb_missileturret (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="missileturret" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_missileturret", "missileturret" ); + +/*QUAKED trigger_multiple_bcs_sfb_table (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="table" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_table", "table" ); + +/*QUAKED trigger_multiple_bcs_sfb_truck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_truck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_truck", "sfb_truck" ); + +/*QUAKED trigger_multiple_bcs_sfb_vtol (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="vtol" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_vtol", "vtol" ); + +/*QUAKED trigger_multiple_bcs_sfb_wreckage (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="wreckage" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_wreckage", "wreckage" ); + +/*QUAKED trigger_multiple_bcs_sfb_cargocrane (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cargocrane" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_cargocrane", "cargocrane" ); + +/*QUAKED trigger_multiple_bcs_sfb_catwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_catwalk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_catwalk", "sfb_catwalk" ); + +/*QUAKED trigger_multiple_bcs_sfb_console (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_console" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_console", "sfb_console" ); + +/*QUAKED trigger_multiple_bcs_sfb_jammer (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_jammer" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_jammer", "sfb_jammer" ); + +/*QUAKED trigger_multiple_bcs_sfb_launchpad (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_launchpad" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_launchpad", "sfb_launchpad" ); + +/*QUAKED trigger_multiple_bcs_sfb_jet (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sfb_jet" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_sfb_jet", "sfb_jet" ); + +} + +greece_locations() +{ +/*QUAKED trigger_multiple_bcs_grk_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_balcony" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_balcony", "grk_balcony" ); + +/*QUAKED trigger_multiple_bcs_grk_1stfloor_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_1stfloor_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_1stfloor_window", "grk_1stfloor_window" ); + +/*QUAKED trigger_multiple_bcs_grk_2ndfloor_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_2ndfloor_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_2ndfloor_window", "grk_2ndfloor_window" ); + +/*QUAKED trigger_multiple_bcs_grk_3rdfloor_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_3rdfloor_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_3rdfloor_window", "grk_3rdfloor_window" ); + +/*QUAKED trigger_multiple_bcs_grk_cafe (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_cafe" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_cafe", "grk_cafe" ); + +/*QUAKED trigger_multiple_bcs_grk_rooftop (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_rooftop" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_rooftop", "grk_rooftop" ); + + /*QUAKED trigger_multiple_bcs_grk_doorway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="grk_doorway" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_grk_doorway", "grk_doorway" ); + +/*QUAKED trigger_multiple_bcs_gre_vehic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="gre_vehic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_gre_vehic", "gre_vehic" ); + +/*QUAKED trigger_multiple_bcs_gre_statue (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="gre_statue" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_gre_statue", "gre_statue" ); +} + +seoul_locations() +{ + /*QUAKED trigger_multiple_bcs_seo_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_balcony" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_balcony", "seo_balcony"); + + /*QUAKED trigger_multiple_bcs_seo_bus (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_bus" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_bus", "seo_bus"); + + /*QUAKED trigger_multiple_bcs_seo_stairs (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_stairs" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_stairs", "seo_stairs"); + + /*QUAKED trigger_multiple_bcs_seo_2ndwindow (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_2ndwindow" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_2ndwindow", "seo_2ndwindow"); + + /*QUAKED trigger_multiple_bcs_seo_3rdbalcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_3rdbalcony" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_3rdbalcony", "seo_3rdbalcony"); + + /*QUAKED trigger_multiple_bcs_seo_sculpture (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_sculpture" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_sculpture", "seo_sculpture"); + + /*QUAKED trigger_multiple_bcs_seo_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_window", "seo_window"); + + /*QUAKED trigger_multiple_bcs_seo_window (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_window" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_window", "seo_window"); + + /*QUAKED trigger_multiple_bcs_seo_cafe (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_cafe" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_cafe", "seo_cafe"); + + /*QUAKED trigger_multiple_bcs_seo_counter (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="seo_counter" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_seo_counter", "seo_counter"); + +} + +detroit_locations() +{ + /*QUAKED trigger_multiple_bcs_det_bar (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="det_bar" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_det_bar", "det_bar"); + + /*QUAKED trigger_multiple_bcs_det_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="det_balcony" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_det_balcony", "det_balcony"); + + /*QUAKED trigger_multiple_bcs_det_walkway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="det_walkway" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_det_walkway", "det_walkway"); + + /*QUAKED trigger_multiple_bcs_det_ambulance (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="det_ambulance" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_det_ambulance", "det_ambulance"); +} + +betrayal_locations() +{ + /*QUAKED trigger_multiple_bcs_bet_plaza (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_plaza" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_plaza", "bet_plaza"); + + /*QUAKED trigger_multiple_bcs_bet_way (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_way" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_way", "bet_way"); + + /*QUAKED trigger_multiple_bcs_bet_1stfloor (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_1stfloor" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_1stfloor", "bet_1stfloor"); + + /*QUAKED trigger_multiple_bcs_bet_2ndfloor (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_2ndfloor" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_2ndfloor", "bet_2ndfloor"); + + /*QUAKED trigger_multiple_bcs_bet_3rdfloor (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_3rdfloor" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_3rdfloor", "bet_3rdfloor"); + + /*QUAKED trigger_multiple_bcs_bet_above (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_above" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_above", "bet_above"); + + /*QUAKED trigger_multiple_bcs_bet_balcony (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_balcony" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_balcony", "bet_balcony"); + + /*QUAKED trigger_multiple_bcs_bet_barge (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_barge" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_barge", "bet_barge"); + + /*QUAKED trigger_multiple_bcs_bet_checkpt (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_checkpt" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_checkpt", "bet_checkpt"); + + /*QUAKED trigger_multiple_bcs_bet_doorway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_doorway" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_doorway", "bet_doorway"); + + /*QUAKED trigger_multiple_bcs_bet_open (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_open" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_open", "bet_open"); + + /*QUAKED trigger_multiple_bcs_bet_patio (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_patio" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_patio", "bet_patio"); + + /*QUAKED trigger_multiple_bcs_bet_rooftop (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_rooftop" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_rooftop", "bet_rooftop"); + + /*QUAKED trigger_multiple_bcs_bet_street (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_street" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_street", "bet_street"); + + /*QUAKED trigger_multiple_bcs_bet_drones (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_drones" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_drones", "bet_drones"); + + /*QUAKED trigger_multiple_bcs_bet_fountain (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_fountain" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_fountain", "bet_fountain"); + + /*QUAKED trigger_multiple_bcs_bet_skybridge (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_skybridge" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_skybridge", "bet_skybridge"); + + /*QUAKED trigger_multiple_bcs_bet_below (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_below" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_below", "bet_below"); + + /*QUAKED trigger_multiple_bcs_bet_bldng (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_bldng" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_bldng", "bet_bldng"); + + /*QUAKED trigger_multiple_bcs_bet_bridge (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_bridge" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_bridge", "bet_bridge"); + + /*QUAKED trigger_multiple_bcs_bet_deplycover (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_deplycover" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_deplycover", "bet_deplycover"); + + /*QUAKED trigger_multiple_bcs_bet_pallets (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_pallets" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_pallets", "bet_pallets"); + + /*QUAKED trigger_multiple_bcs_bet_catwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_catwalk" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_catwalk", "bet_catwalk"); + + /*QUAKED trigger_multiple_bcs_bet_table (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_table" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_table", "bet_table"); + + /*QUAKED trigger_multiple_bcs_bet_crates (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_crates" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_crates", "bet_crates"); + + /*QUAKED trigger_multiple_bcs_bet_dock (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_dock" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_dock", "bet_dock"); + + /*QUAKED trigger_multiple_bcs_bet_leftshore (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_leftshore" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_leftshore", "bet_leftshore"); + + /*QUAKED trigger_multiple_bcs_bet_rightshore (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bet_rightshore" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_bet_rightshore", "bet_rightshore"); +} + +lab_locations() +{ + /*QUAKED trigger_multiple_bcs_lab_canisters (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_canisters" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_canisters", "lab_canisters"); + + /*QUAKED trigger_multiple_bcs_lab_camera (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_camera" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_camera", "lab_camera"); + + /*QUAKED trigger_multiple_bcs_lab_van (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_van" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_van", "lab_van"); + + /*QUAKED trigger_multiple_bcs_lab_lwrcatwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_lwrcatwalk" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_lwrcatwalk", "lab_lwrcatwalk"); + + /*QUAKED trigger_multiple_bcs_lab_uprcatwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_uprcatwalk" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_uprcatwalk", "lab_uprcatwalk"); + + /*QUAKED trigger_multiple_bcs_lab_forklift (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_forklift" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_forklift", "lab_forklift"); + + /*QUAKED trigger_multiple_bcs_lab_rooftop (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lab_rooftop" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lab_rooftop", "lab_rooftop"); +} + +recovery_locations() +{ + /*QUAKED trigger_multiple_bcs_rec_firepit (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_firepit" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_firepit", "rec_firepit"); + + /*QUAKED trigger_multiple_bcs_rec_hill (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_hill" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_hill", "rec_hill"); + + /*QUAKED trigger_multiple_bcs_rec_pool (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_pool" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_pool", "rec_pool"); + + /*QUAKED trigger_multiple_bcs_rec_road (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_road" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_road", "rec_road"); + + /*QUAKED trigger_multiple_bcs_rec_patio (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_patio" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_patio", "rec_patio"); + + /*QUAKED trigger_multiple_bcs_rec_table (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_table" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_table", "rec_table"); + + /*QUAKED trigger_multiple_bcs_rec_couch (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rec_couch" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_rec_couch", "rec_couch"); +} + +lagos_locations() +{ + /*QUAKED trigger_multiple_bcs_lag_street (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_street" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_street", "lag_street"); + + /*QUAKED trigger_multiple_bcs_lag_dogs (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_dogs" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_dogs", "lag_dogs"); + + /*QUAKED trigger_multiple_bcs_lag_median (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_median" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_median", "lag_median"); + + /*QUAKED trigger_multiple_bcs_lag_rpgbus (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_rpgbus" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_rpgbus", "lag_rpgbus"); + + /*QUAKED trigger_multiple_bcs_lag_overpass (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_overpass" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_overpass", "lag_overpass"); + + /*QUAKED trigger_multiple_bcs_lag_suv (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_suv" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_suv", "lag_suv"); + + /*QUAKED trigger_multiple_bcs_lag_suvapproach (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_suvapproach" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_suvapproach", "lag_suvapproach"); + + /*QUAKED trigger_multiple_bcs_lag_topvan (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lag_topvan" +*/ + add_bcs_location_mapping ( "trigger_multiple_bcs_lag_topvan", "lag_topvan"); +} + +merida() +{ +/*QUAKED trigger_multiple_bcs_mp_merida_radiotower (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="radiotower" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_radiotower", "radiotower" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_embassy_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_embassy_generic", "embassy_generic" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_aaguns (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="aaguns" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_aaguns", "aaguns" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_tunnel (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="tunnel" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_tunnel", "tunnel" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_cannons_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cannons_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_cannons_generic", "cannons_generic" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_pool (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="pool" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_pool", "pool" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_embassy_north (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_north" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_embassy_north", "embassy_north" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_embassy_south (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_south" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_embassy_south", "embassy_south" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_embassy_east (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_east" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_embassy_east", "embassy_east" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_embassy_west (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_west" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_embassy_west", "embassy_west" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_cannons_embassy (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cannons_embassy" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_cannons_embassy", "cannons_embassy" ); + +/*QUAKED trigger_multiple_bcs_mp_merida_cannons_radiotower (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cannons_radiotower" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_merida_cannons_radiotower", "cannons_radiotower" ); +} + +//--------------------------------------------------------- +// - BLACKSMITH MULTIPLAYER - +//--------------------------------------------------------- + +laser2_mp() +{ + +/*QUAKED trigger_multiple_bcs_mp_lsr_radardish (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lsr_radardish" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_radardish", "lsr_radardish" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_laserairdefensegun (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lsr_laserairdefensegun" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_laserairdefensegun", "lsr_laserairdefensegun" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_razorback (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lsr_razorback" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_razorback", "lsr_razorback" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_underhelipad (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_underhelipad" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_underhelipad", "lsr_underhelipad" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bunkerlookout (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bunkerlookout" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bunkerlookout", "lsr_bunkerlookout" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_inradartower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_inradartower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_inradartower", "lsr_inradartower" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_byradartower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_byradartower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_byradartower", "lsr_byradartower" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_shippingcontainer (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_shippingcontainer" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_shippingcontainer", "lsr_shippingcontainer" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_onhelipad (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_onhelipad" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_onhelipad", "lsr_onhelipad" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_onbeach (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_onbeach" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_onbeach", "lsr_onbeach" ); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bybridge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bybridge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_onbeach", "lsr_bybridge"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bylasergenerator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bylasergenerator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bylasergenerator", "lsr_bylasergenerator"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_underlaser (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_underlaser" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_underlaser", "lsr_underlaser"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_byhelipad (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_byhelipad" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_byhelipad", "lsr_byhelipad"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_electricalroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_electricalroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_electricalroom", "lsr_electricalroom"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_piperoom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_piperoom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_piperoom", "lsr_piperoom"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bybuoy (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bybuoy" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bybuoy", "lsr_bybuoy"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bygenerator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bygenerator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bygenerator", "lsr_bygenerator"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_offswitch (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_offswitch" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_offswitch", "lsr_offswitch"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_missilerack (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_missilerack" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_missilerack", "lsr_missilerack"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_underchains (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_underchains" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_underchains", "lsr_underchains"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_beachbunkerrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_beachbunkerrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_beachbunkerrooftop", "lsr_beachbunkerrooftop"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bunkerrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bunkerrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bunkerrooftop", "lsr_bunkerrooftop"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_nettedcargo (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_nettedcargo" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_nettedcargo", "lsr_nettedcargo"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_cagedcargo (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_cagedcargo" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_cagedcargo", "lsr_cagedcargo"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_overturnedcar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_overturnedcar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_overturnedcar", "lsr_overturnedcar"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_bridgebase (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_bridgebase" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_bridgebase", "lsr_bridgebase"); + +/*QUAKED trigger_multiple_bcs_mp_lsr_cornerbunker (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lsr_cornerbunker" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lsr_cornerbunker", "lsr_cornerbunker"); +} + +lab2_mp() +{ + +/*QUAKED trigger_multiple_bcs_mp_lab_parkinglot (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_parkinglot" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_parkinglot", "lab_parkinglot"); + +/*QUAKED trigger_multiple_bcs_mp_lab_serverroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_serverroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_serverroom", "lab_serverroom"); + +/*QUAKED trigger_multiple_bcs_mp_lab_holoroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_holoroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_holoroom", "lab_holoroom"); + +/*QUAKED trigger_multiple_bcs_mp_lab_dryingroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_dryingroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_dryingroom", "lab_dryingroom"); + +/*QUAKED trigger_multiple_bcs_mp_lab_chemvats (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_chemvats" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_chemvats", "lab_chemvats"); + +/*QUAKED trigger_multiple_bcs_mp_lab_generator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_generator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_generator", "lab_generator"); + +/*QUAKED trigger_multiple_bcs_mp_lab_lobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_generator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_lobby", "lab_lobby"); + +/*QUAKED trigger_multiple_bcs_mp_lab_behindcrates (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_behindcrates" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_behindcrates", "lab_behindcrates"); + +/*QUAKED trigger_multiple_bcs_mp_lab_scienceroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_scienceroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_scienceroom", "lab_scienceroom"); + +/*QUAKED trigger_multiple_bcs_mp_lab_storageroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_storageroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_storageroom", "lab_storageroom"); + +/*QUAKED trigger_multiple_bcs_mp_lab_trench (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_trench" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_trench", "lab_trench"); + +/*QUAKED trigger_multiple_bcs_mp_lab_behindvehicle (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lab_behindvehicle" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lab_behindvehicle", "lab_behindvehicle"); +} + + +mp_refraction() +{ +/*QUAKED trigger_multiple_bcs_mp_ref_oncrane (0 0.25 0.5)? + defaulttexture="bcs" + soundalias="ref_oncrane" + */ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_oncrane", "ref_oncrane"); + +/*QUAKED trigger_multiple_bcs_mp_ref_byloadingdocks (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_byloadingdocks" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_byloadingdocks", "ref_byloadingdocks"); + +/*QUAKED trigger_multiple_bcs_mp_ref_nearelevatorentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_nearelevatorentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_nearelevatorentrance", "ref_nearelevatorentrance"); + +/*QUAKED trigger_multiple_bcs_mp_ref_inelevator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_inelevator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_inelevator", "ref_inelevator"); + +/*QUAKED trigger_multiple_bcs_mp_ref_oncatwalks (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_oncatwalks" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_oncatwalks", "ref_oncatwalks"); + +/*QUAKED trigger_multiple_bcs_mp_ref_seccheckpoint (0 0.25 0.5)? +defaulttextture="bcs" +soundalias="ref_seccheckpoint" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_seccheckpoint", "ref_seccheckpoint"); + +/*QUAKED trigger_multiple_bcs_mp_ref_helopadentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_helopadentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_helopadentrance", "ref_helopadentracne"); + +/*QUAKED trigger_multiple_bcs_mp_ref_checkinstation (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_checkinstation" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_checkinstation", "ref_checkinstation"); + +/*QUAKED trigger_multiple_bcs_mp_ref_maintenaceentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_maintenceentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_maintenceentrance", "ref_maintenceentrance"); + +/*QUAKED trigger_multiple_bcs_mp_ref_insidelounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_insidelounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_insidelounge", "ref_insidelounge"); + +/*QUAKED trigger_multiple_bcs_mp_ref_bylockmechanism (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_bylockmechanism" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_bylockmechanism", "ref_bylockmechanism"); + +/*QUAKED trigger_multiple_bcs_mp_ref_inelevatorshaft (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_inelevatorshaft" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_inelevatorshaft", "ref_inelevatorshaft"); + +/*QUAKED trigger_multiple_bcs_mp_ref_underhelipad (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_underhelipad" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_underhelipad", "ref_underhelipad"); + +/*QUAKED trigger_multiple_bcs_mp_ref_bylgcontainers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_bylgcontainers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_bylgcontainers", "ref_bylgcontainers"); + +/*QUAKED trigger_multiple_bcs_mp_ref_bylockers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_bylockers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_bylockers", "ref_bylockers"); + +/*QUAKED trigger_multiple_bcs_mp_ref_abovelockers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_abovelockers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_abovelockers", "ref_abovelockers"); + +/*QUAKED trigger_multiple_bcs_mp_ref_byadtowers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ref_byadtowers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ref_byadtowers", "ref_byadtowers"); +} + +mp_prison() +{ +/*QUAKED trigger_multiple_bcs_mp_psn_insidecellblock (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_insidecellblock" +*/ + + add_bcs_location_mapping(" trigger_multiple_bcs_mp_psn_insidecellblock", "psn_insidecellblock"); + +/*QUAKED trigger_multiple_bcs_mp_psn_psnfrontentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_psnfrontentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_psnfrontentrance", "psn_psnfrontentrance"); + +/*QUAKED trigger_multiple_bcs_mp_psn_insidemaintenancebldg (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_insidemaintenancebldg" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_insidemaintenancebldg", "psn_insidemaintenancebldg"); + +/*QUAKED trigger_multiple_bcs_mp_psn_onmaintenancebldgroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_onmaintenancebldgroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_onmaintenancebldgroof", "psn_onmaintenancebldgroof"); + +/*QUAKED trigger_multiple_bcs_mp_psn_prisonyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_prisonyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_prisonyard", "psn_prisonyard"); + +/*QUAKED trigger_multiple_bcs_mp_psn_bballcourt (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_bballcourt" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_bballcourt", "psn_bballcourt"); + +/*QUAKED trigger_multiple_bcs_mp_psn_psnbus (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_psnbus" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_psnbus", "psn_psnbus"); + +/*QUAKED trigger_multiple_bcs_mp_psn_sectower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_sectower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_sectower", "psn_sectower"); + +/*QUAKED trigger_multiple_bcs_mp_psn_insidegarage (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_insidegarage" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_insidegarage", "psn_insidegarage"); + +/*QUAKED trigger_multiple_bcs_mp_psn_topofgarage (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_topofgarage" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_topofgarage", "psn_topofgarage"); + +/*QUAKED trigger_multiple_bcs_mp_psn_destroyedwall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_destroyedwall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_destroyedwall", "psn_destroyedwall"); + + +/*QUAKED trigger_multiple_bcs_mp_psn_cellblockcatwalk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_cellblockcatwalk" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_cellblockcatwalk", "psn_cellblockcatwalk"); + +/*QUAKED trigger_multiple_bcs_mp_psn_lobbyentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_lobbyentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_lobbyentrance", "psn_lobbyentrance"); + +/*QUAKED trigger_multiple_bcs_mp_psn_electricalbox (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_electricalbox" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_electricalbox", "psn_electricalbox"); + +/*QUAKED trigger_multiple_bcs_mp_psn_policecar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_policecar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_policecar", "psn_policecar"); + +/*QUAKED trigger_multiple_bcs_mp_psn_behinddumpster (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_behinddumpster" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_behinddumpster", "psn_behinddumpster"); + +/*QUAKED trigger_multiple_bcs_mp_psn_catwalk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_catwalk" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_catwalk", "psn_catwalk"); + +/*QUAKED trigger_multiple_bcs_mp_psn_laundrybin (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_laundrybin" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_laundrybin", "psn_laundrybin"); + +/*QUAKED trigger_multiple_bcs_mp_psn_psnrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_psnrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_psnrooftop", "psn_psnrooftop"); + +/*QUAKED trigger_multiple_bcs_mp_psn_behindac (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_behindac" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_behindac", "psn_behindac"); + +/*QUAKED trigger_multiple_bcs_mp_psn_insidelockerbldg (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="psn_insidelockerbldg" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_psn_insidelockerbldg", "psn_insidelockerbldg"); +} + +mp_dam() +{ +/*QUAKED trigger_multiple_bcs_mp_dam_frontgate (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_frontgate" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_frontgate", "dam_frontgate"); + +/*QUAKED trigger_multiple_bcs_mp_dam_semitruck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_semitruck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_semitruck", "dam_semitruck"); + +/*QUAKED trigger_multiple_bcs_mp_dam_constructionsite (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_constructionsite" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_contructionsite", "dam_contructionsite"); + +/*QUAKED trigger_multiple_bcs_mp_dam_insideoffice (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_insideoffice" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_insideoffice", "dam_insideoffice"); + +/*QUAKED trigger_multiple_bcs_mp_dam_oncatwalks (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_oncatwalks" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_oncatwalks", "dam_oncatwalks"); + +/*QUAKED trigger_multiple_bcs_mp_dam_centerstreet (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_centerstreet" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_centerstreet", "dam_centerstreet"); + +/*QUAKED trigger_multiple_bcs_mp_dam_onminigun (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_onminigun" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_onminigun", "dam_onminigun"); + +/*QUAKED trigger_multiple_bcs_mp_dam_cranebase (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_cranebase" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_cranebase", "dam_cranebase"); + +/*QUAKED trigger_multiple_bcs_mp_dam_infactory (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_infactory" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_infactory", "dam_infactory"); + +/*QUAKED trigger_multiple_bcs_mp_dam_inturbinebldg (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_inturbinebldg" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_inturbinebldg", "dam_inturbinebldg"); + +/*QUAKED trigger_multiple_bcs_mp_dam_movingpipe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_movingpiep" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_movingpipe", "dam_movingpipe"); + +/*QUAKED trigger_multiple_bcs_mp_dam_electricalgrid (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_electricalgrid" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_electricalgrid", "dam_electricalgrid"); + +/*QUAKED trigger_multiple_bcs_mp_dam_insidelounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_insidelounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_dam_insidelounge", "dam_insidelounge"); + +/*QUAKED trigger_multiple_bcs_mp_dam_inpipes (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_inpipes" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_inpipes", "dam_inpipes"); + +/*QUAKED trigger_multiple_bcs_mp_dam_underground (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_underground" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_underground", "dam_underground"); + +/*QUAKED trigger_multiple_bcs_mp_dam_factoryrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="dam_factoryrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dam_factoryrooftop", "dam_factoryrooftop"); + +} + +mp_detroit() +{ +/*QUAKED trigger_multiple_bcs_mp_det_inschool (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_inschool" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_inschool", "det_inschool"); + +/*QUAKED trigger_multiple_bcs_mp_det_bylockers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_bylockers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_bylockers", "det_bylockers"); + +/*QUAKED trigger_multiple_bcs_mp_det_hospitalentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_hospitalentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_hospitalentrance", "det_hospitalentrance"); + +/*QUAKED trigger_multiple_bcs_mp_det_hospitallobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_hospitallobby" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_hospitallobby", "det_hospitallobby"); + +/*QUAKED trigger_multiple_bcs_mp_det_inparkinggarage (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_inparkinggarage" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_inparkinggarage", "det_inparkinggarage"); + +/*QUAKED trigger_multiple_bcs_mp_det_garageentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_garageentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_garageentrance", "det_garageentrance"); + +/*QUAKED trigger_multiple_bcs_mp_det_piperoom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_piperoom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_piperoom", "det_piperoom"); + +/*QUAKED trigger_multiple_bcs_mp_det_onramp (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_onramp" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_onramp", "det_onramp"); + +/*QUAKED trigger_multiple_bcs_mp_det_inalley (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_inalley" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_inalley", "det_inalley"); + +/*QUAKED trigger_multiple_bcs_mp_det_bytrailers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_bytrailers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_bytrailers", "det_bytrailers"); + +/*QUAKED trigger_multiple_bcs_mp_det_ontrailers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_ontrailers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_ontrailers", "det_ontrailers"); + +/*QUAKED trigger_multiple_bcs_mp_det_inthepod (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_inthepod" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_inthepod", "det_inthepod"); + +/*QUAKED trigger_multiple_bcs_mp_det_throughpark (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_throughpark" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_throughpark", "det_throughpark"); + +/*QUAKED trigger_multiple_bcs_mp_det_byplayground (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_byplayground" +*/ + +add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_byplayground", "det_byplayground"); + +/*QUAKED trigger_multiple_bcs_mp_det_garageoverlook (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_garageoverlook" +*/ + + add_bcs_location_mapping( "triger_multiple_bcs_mp_det_garageoverlook", "det_garageoverlook"); + +/*QUAKED trigger_multiple_bcs_mp_det_backalley (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_backalley" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_backalley", "det_backalley"); + +/*QUAKED trigger_multiple_bcs_mp_det_parkoffice (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_parkoffice" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_parkoffice", "det_parkoffice"); + +/*QUAKED trigger_multiple_bcs_mp_det_hospitaloffice (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_hospitaloffice" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_hospitaloffice", "det_hospitaloffice"); + +/*QUAKED trigger_multiple_bcs_mp_det_throughstreet (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_throughstreet" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_throughstreet", "det_throughstreet"); + +/*QUAKED trigger_multiple_bcs_mp_det_upperstreet (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_upperstreet" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_upperstreet", "det_upperstreet"); + +/*QUAKED trigger_multiple_bcs_mp_det_lowerstreet (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_lowerstreet" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_lowerstreet", "det_lowerstreet"); + +/*QUAKED trigger_multiple_bcs_mp_det_bycontainers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_bycontainers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_bycontainers", "det_bycontainers"); + +/*QUAKED trigger_multiple_bcs_mp_det_bydiner (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="det_bydiner" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_det_bydiner", "det_bydiner"); + +} + +mp_greenband() +{ +/*QUAKED trigger_multiple_bcs_mp_grn_2ndflooraquarium (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_2ndflooraquarium" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_2ndflooraquarium", "grn_2ndflooraquarium"); + +/*QUAKED trigger_multiple_bcs_mp_grn_hotelbar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_hotelbar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_hotelbar", "grn_hotelbar"); + +/*QUAKED trigger_multiple_bcs_mp_grn_incafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_incafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_incafe", "grn_incafe"); + +/*QUAKED trigger_multiple_bcs_mp_grn_behindcherrytree (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_behindcherrytree" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_behindcherrytree", "grn_behindcherrytree"); + +/*QUAKED trigger_multiple_bcs_mp_grn_aquariumhallway (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_aquariumhallway" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_aquariumhallway", "grn_aquariumhallway"); + +/*QUAKED trigger_multiple_bcs_mp_grn_aquariumpatio (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_aquariumpatio" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_aquariumpatio", "grn_aquariumpatio"); + +/*QUAKED trigger_multiple_bcs_mp_grn_inelevator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_inelevator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_inelevator", "grn_inelevator"); + +/*QUAKED trigger_multiple_bcs_mp_grn_behindaquariumdesk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_behindaquariumdesk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_behindaquariumdesk", "grn_behindaquariumdesk"); + +/*QUAKED trigger_multiple_bcs_mp_grn_inzengarden (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_inzengarden" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_inzengarden", "grn_inzengarden"); + +/*QUAKED trigger_multiple_bcs_mp_grn_topofzengarden (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_topofzengarden" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_topofzengarden", "grn_topofzengarden"); + +/*QUAKED trigger_multiple_bcs_mp_grn_hotelentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_hotelentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_hotelentrance", "grn_hotelentrance"); + +/*QUAKED trigger_multiple_bcs_mp_grn_hotellobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_hotellobby" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_hotellobby", "grn_hotellobby"); + +/*QUAKED trigger_multiple_bcs_mp_grn_inaquarium (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_inaquarium" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_inaquarium", "grn_aquarium"); + +/*QUAKED trigger_multiple_bcs_mp_grn_inlounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_inlounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_inlounge", "grn_inlounge"); + +/*QUAKED trigger_multiple_bcs_mp_grn_behindstatue (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_behindstatue" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_behindstatue", "grn_behindstatue"); + +/*QUAKED trigger_multiple_bcs_mp_grn_insidewalkway (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_insidewalkway" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_insidewalkway", "grn_insidewalkway"); + +/*QUAKED trigger_multiple_bcs_mp_grn_underawning (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_underawning" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_underawning", "grn_underawning"); + +/*QUAKED trigger_multiple_bcs_mp_grn_topofawning (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_topofawning" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_topofawning", "grn_topofawning"); + +/*QUAKED trigger_multiple_bcs_mp_grn_behindlargerock (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_behindlargerock" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_behindlargerock", "grn_behindlargerock"); + +/*QUAKED trigger_multiple_bcs_mp_grn_nearsculpture (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_nearsculpture" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_nearsculpture", "grn_nearsculpture"); + +/*QUAKED trigger_multiple_bcs_mp_grn_behindsmallplanter (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="grn_behindsmallplanter" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_grn_behindsmallplanter", "grn_behindsmallplanter"); + +} + +mp_instinct() +{ +/*QUAKED trigger_multiple_bcs_mp_ins_byriverbed (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_byriverbed +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_byriverbed", "ins_byriverbed"); + +/*QUAKED trigger_multiple_bcs_mp_ins_underexcavator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_underexcavator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_underexcavator", "ins_underexcavator"); + +/*QUAKED trigger_multiple_bcs_mp_ins_oncliffs (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_oncliffs" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_oncliffs", "ins_oncliffs"); + +/*QUAKED trigger_multiple_bcs_mp_ins_centertemple (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_centertemple" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_centertemple", "ins_centertemple"); + +/*QUAKED trigger_multiple_bcs_mp_ins_towerofruins (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_towerofruins" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_towerofruins", "ins_towerofruins"); + +/*QUAKED trigger_multiple_bcs_mp_ins_nearpryramidhall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_nearpryramidhall" +*/ + + add_bcs_location_mapping( "triger_multiple_bcs_mp_ins_nearpryramidhall", "ins_nearpryramidhall"); + +/*QUAKED trigger_multiple_bcs_mp_ins_insidepryramid (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_insidepryramid" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_insidepryramid", "ins_insidepryramid"); + +/*QUAKED trigger_multiple_bcs_mp_ins_behindtrailers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_behindtrailers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_behindtrailers", "ins_behindtrailers"); + +/*QUAKED trigger_multiple_bcs_mp_ins_powergenerator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_powergenerators" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_powergenerators", "ins_powergenerators"); + +/*QUAKED trigger_multiple_bcs_mp_ins_onrubble (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="ins_onrubble" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_ins_onrubble", "ins_onrubble"); + +} + +mp_levity() +{ +/*QUAKED trigger_multiple_bcs_mp_lev_secgates (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_secgates" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_secgates", "lev_secgates"); + +/*QUAKED trigger_multiple_bcs_mp_lev_controlroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_controlroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_controlroom", "lev_controlroom"); + +/*QUAKED trigger_multiple_bcs_mp_lev_accessroofs (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_accessroofs" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_accessroofs", "lev_accessroofs"); + +/*QUAKED trigger_multiple_bcs_mp_lev_mainhangar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_mainhangar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_mainhangar", "lev_mainhangar"); + +/*QUAKED trigger_multiple_bcs_mp_lev_dronestorage (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_dronestorage" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_dronestorage", "lev_dronestorage"); + +/*QUAKED trigger_multiple_bcs_mp_lev_inflightcontrol (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_inflightcontrol" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_inflightcontrol", "lev_flightcontrol"); + +/*QUAKED trigger_multiple_bcs_mp_lev_flightcontrolroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_flightcontrolroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_flightcontrolroof", "lev_flightcontrolroof"); + +/*QUAKED trigger_multiple_bcs_mp_lev_flightcontrolalley (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_flightcontrolalley" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_flightcontrolalley", "lev_flightcontrolalley"); + +/*QUAKED trigger_multiple_bcs_mp_lev_observationdeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_observationdeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_observationdeck", "lev_observationdeck"); + +/*QUAKED trigger_multiple_bcs_mp_lev_severaccessroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_serveraccessroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_serveraccessroof", "lev_serveraccessroof"); + +/*QUAKED trigger_multiple_bcs_mp_lev_hangarlounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_hangarlounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_hangarlounge", "lev_hangarlounge"); + +/*QUAKED trigger_multiple_bcs_mp_lev_powercontrol (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_powercontrol" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_powercontrol", "lev_powercontrol"); + +/*QUAKED trigger_multiple_bcs_mp_lev_readyrooms (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_readyrooms" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_readyrooms", "lev_readyrooms"); + +/*QUAKED trigger_multiple_bcs_mp_lev_rearhangar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_rearhangar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_rearhangar", "lev_rearhangar"); + +/*QUAKED trigger_multiple_bcs_mp_lev_serveraccess (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_serveraccess" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_serveraccess", "lev_serveraccess"); + +/*QUAKED trigger_multiple_bcs_mp_lev_nearwaterfall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_nearwaterfall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_nearwaterfall", "lev_nearwaterfall"); + +/*QUAKED trigger_multiple_bcs_mp_lev_nearrockslide (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="lev_nearrockslide" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_lev_nearrockslide", "lev_nearrockslide"); + +} + +mp_recovery() +{ +/*QUAKED trigger_multiple_bcs_mp_rec_inobservatory (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_inobservatory" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_inobservatory", "rec_inobservatory"); + +/*QUAKED trigger_multiple_bcs_mp_rec_bytram (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_bytram" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_bytram", "rec_bytram"); + +/*QUAKED trigger_multiple_bcs_mp_rec_byskywalk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_byskywalk" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_byskywalk", "rec_byskywalk"); + +/*QUAKED trigger_multiple_bcs_mp_rec_inravine (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_inravine" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_inravine", "rec_inravine"); + +/*QUAKED trigger_multiple_bcs_mp_rec_insiderockhall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_insiderockhall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_insiderockhall", "rec_insiderockhall"); + +/*QUAKED trigger_multiple_bcs_mp_rec_byhangardoor (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_byhangardoor" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_byhangardoor", "rec_byhangardoor"); + +/*QUAKED trigger_multiple_bcs_mp_rec_inplaza (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_inplaza" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_inplaza", "rec_inplaza"); + +/*QUAKED trigger_multiple_bcs_mp_rec_bychopper (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_bychopper" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_bychopper", "rec_bychopper"); + +/*QUAKED trigger_multiple_bcs_mp_rec_onlaunchdeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_onlaunchdeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_onlaunchdeck", "rec_onlaunchdeck"); + +/*QUAKED trigger_multiple_bcs_mp_rec_nearridge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_nearridge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_nearridge", "rec_nearridge"); + +/*QUAKED trigger_multiple_bcs_mp_rec_undercontroltower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_undercontroltower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_undercontroltower", "rec_undercontroltower"); + +/*QUAKED trigger_multiple_bcs_mp_rec_gatecontrolroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_gatecontrolroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_gatecontrolroom", "rec_gatecontrolroom"); + +/*QUAKED trigger_multiple_bcs_mp_rec_underdeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_underdeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_underdeck", "rec_underdeck"); + +/*QUAKED trigger_multiple_bcs_mp_rec_observationdeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_observationdeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_observationdeck", "rec_observationdeck"); + +/*QUAKED trigger_multiple_bcs_mp_rec_byrustedvan (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="rec_byrustedvan" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_rec_byrustedvan", "rec_byrustedvan"); + +} + +mp_solar() +{ +/*QUAKED trigger_multiple_bcs_mp_slr_inparkinglot (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_inparkinglot" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_inparkinglot", "slr_inparkinglot"); + +/*QUAKED trigger_multiple_bcs_mp_slr_bymainentry (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_bymainentry" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_bymainentry", "slr_bymainentry"); + +/*QUAKED trigger_multiple_bcs_mp_slr_atpool (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_atpool" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_atpool", "slr_atpool"); + +/*QUAKED trigger_multiple_bcs_mp_slr_indraintunnel (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_indraintunnel" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_indraintunnel", "slr_indraintunnel"); + +/*QUAKED trigger_multiple_bcs_mp_slr_inoffice (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_inoffice" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_inoffice", "slr_inoffice"); + +/*QUAKED trigger_multiple_bcs_mp_slr_bysmalltanks (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_bysmalltanks" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_bysmalltanks", "slr_bysmalltanks"); + +/*QUAKED trigger_multiple_bcs_mp_slr_intowercontrolroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_intowercontrolroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_intowercontrolroom", "slr_intowercontrolroom"); + +/*QUAKED trigger_multiple_bcs_mp_slr_bytransformers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_bytransformers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_bytransformers", "slr_bytransformers"); + +/*QUAKED trigger_multiple_bcs_mp_slr_condensercontrolroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_condensercontrolroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_condensercontrolroom", "slr_condensercontrolroom"); + +/*QUAKED trigger_multiple_bcs_mp_slr_inbacklot (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_inbacklot" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_inbacklot", "slr_inbacklot"); + +/*QUAKED trigger_multiple_bcs_mp_slr_bycyclonetank (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_bycyclonetank" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_bycyclonetank", "slr_bycyclonetank"); + +/*QUAKED trigger_multiple_bcs_mp_slr_indrainarea (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_indrainarea" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_indrainarea", "slr_indrainarea"); + +/*QUAKED trigger_multiple_bcs_mp_slr_indriveway (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_indriveway" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_indriveway", "slr_indriveway"); + +/*QUAKED trigger_multiple_bcs_mp_slr_visitorcenter (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_visitorcenter" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_visitorcenter", "slr_visitorcenter"); + +/*QUAKED trigger_multiple_bcs_mp_slr_inpumproom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_inpumproom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_inpumproom", "slr_inpumproom"); + +/*QUAKED trigger_multiple_bcs_mp_slr_sciencearea (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_sciencearea" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_sciencearea", "slr_sciencearea"); + +/*QUAKED trigger_multiple_bcs_mp_slr_ingarage (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_ingarage" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_ingarage", "slr_ingarage"); + +/*QUAKED trigger_multiple_bcs_mp_slr_oncatwalks (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_oncatwalks" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_oncatwalks", "slr_oncatwalks"); + +/*QUAKED trigger_multiple_bcs_mp_slr_bycondensers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_bycondensers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_bycondensers", "slr_bycondensers"); + +/*QUAKED trigger_multiple_bcs_mp_slr_inutilityroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_inutilityroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_inutilityroom", "slr_inutilityroom"); + +/*QUAKED trigger_multiple_bcs_mp_slr_onutilityroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="slr_onutilityroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_slr_onutilityroof", "slr_onutilityroof"); + +} + +mp_terrace() +{ +/*QUAKED trigger_multiple_bcs_mp_trc_hotellobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_hotellobby" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_hotellobby", "trc_hotellobby"); + +/*QUAKED trigger_multiple_bcs_mp_trc_atgrotto (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_atgrotto" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_atgrotto", "trc_atgrotto"); + +/*QUAKED trigger_multiple_bcs_mp_trc_insaunatunnel (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_insaunatunnel" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_insaunatunnel", "trc_insaunatunnel"); + +/*QUAKED trigger_multiple_bcs_mp_trc_saunaentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_saunaentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_saunaentrance", "trc_saunaentrance"); + +/*QUAKED trigger_multiple_bcs_mp_trc_inlowerruins (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_inlowerruins" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_inlowerruins", "trc_inlowerruins"); + +/*QUAKED trigger_multiple_bcs_mp_trc_inupperruins (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_inupperruins" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_inupperruins", "trc_inupperruins"); + +/*QUAKED trigger_multiple_bcs_mp_trc_onupperterrace (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_onupperterrace" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_onupperterrace", "trc_onupperterrace"); + +/*QUAKED trigger_multiple_bcs_mp_trc_incafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_incafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_incafe", "trc_incafe"); + +/*QUAKED trigger_multiple_bcs_mp_trc_undertower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_undertower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_undertower", "trc_undertower"); + +/*QUAKED trigger_multiple_bcs_mp_trc_nightclubentrance (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_nightclubentrance" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_nightclubentrance", "trc_nightclubentrance"); + +/*QUAKED trigger_multiple_bcs_mp_trc_yellowroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_yellowroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_yellowroom", "trc_yellowroom"); + +/*QUAKED trigger_multiple_bcs_mp_trc_redroom (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_redroom" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_redroom", "trc_redroom"); + +/*QUAKED trigger_multiple_bcs_mp_trc_lowerterrace (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_lowerterrace" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_lowerterrace", "trc_lowerterrace"); + +/*QUAKED trigger_multiple_bcs_mp_trc_atbonfire (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_atbonfire" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_atbonfire", "trc_atbonfire"); + +/*QUAKED trigger_multiple_bcs_mp_trc_poseidonspool (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_poseidonspool" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_poseidonspool", "trc_poseidonspool"); + +/*QUAKED trigger_multiple_bcs_mp_trc_saunaroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_saunaroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_saunaroof", "trc_saunaroof"); + +/*QUAKED trigger_multiple_bcs_mp_trc_inshowers (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_inshowers" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_inshowers", "trc_inshowers"); + +/*QUAKED trigger_multiple_bcs_mp_trc_topofminervamall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_topofminervamall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_topofminervamall", "trc_topofminervamall"); + +/*QUAKED trigger_multiple_bcs_mp_trc_sixbellspatio (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trc_sixbellspatio" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trc_sixbellspatio", "trc_sixbellspatio"); + +} + +mp_torqued() +{ + +/*QUAKED trigger_multiple_bcs_mp_trq_nearbusstop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_nearbusstop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_nearbusstop", "trq_nearbusstop"); + +/*QUAKED trigger_multiple_bcs_mp_trq_incafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_incafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_incafe", "trq_incafe"); + +/*QUAKED trigger_multiple_bcs_mp_trq_chocolateshop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_chocolateshop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_chocolateshop", "trq_chocolateshop"); + +/*QUAKED trigger_multiple_bcs_mp_trq_baseofclocktower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_baseofclocktower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_baseofclocktower", "trq_baseofclocktower"); + +/*QUAKED trigger_multiple_bcs_mp_trq_inclocktower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_inclocktower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_inclocktower", "trq_inclocktower"); + +/*QUAKED trigger_multiple_bcs_mp_trq_nearfountain (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_nearfountain" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_nearfountain", "trq_nearfountain"); + +/*QUAKED trigger_multiple_bcs_mp_trq_outdoorcafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_outdoorcafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_outdoorcafe", "trq_outdoorcafe"); + +/*QUAKED trigger_multiple_bcs_mp_trq_parkinglot (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_parkinglot" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_parkinglot", "trq_parkinglot"); + +/*QUAKED trigger_multiple_bcs_mp_trq_gatedpatio (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_gatedpatio" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_gatedpatio", "trq_gatedpatio"); + +/*QUAKED trigger_multiple_bcs_mp_trq_nearrestaurant (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_nearrestaurant" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_nearrestaurant", "trq_nearrestaurant"); + +/*QUAKED trigger_multiple_bcs_mp_trq_insemi (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_insemi" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_insemi", "trq_insemi"); + +/*QUAKED trigger_multiple_bcs_mp_trq_nearsnackbar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_nearsnackbar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_nearsnackbar", "trq_nearsnackbar"); + +/*QUAKED trigger_multiple_bcs_mp_trq_insideticketcounter (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_insideticketcounter" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_insideticketcounter", "trq_insideticketcounter"); + +/*QUAKED trigger_multiple_bcs_mp_trq_frontoftrain (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_frontoftrain" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_frontoftrain", "trq_frontoftrain"); + +/*QUAKED trigger_multiple_bcs_mp_trq_backoftrain (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_backoftrain" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_backoftrain", "trq_backoftrain"); + +/*QUAKED trigger_multiple_bcs_mp_trq_utilitywalkway (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_utilitywalkway" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_utilitywalkway", "trq_utilitywalkway"); + +/*QUAKED trigger_multiple_bcs_mp_trq_onskywalk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_onskywalk" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_onskywalk", "trq_onskywalk"); + +/*QUAKED trigger_multiple_bcs_mp_trq_topofawning (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_topofawning" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_topofawning", "trq_topofawning"); + +/*QUAKED trigger_multiple_bcs_mp_trq_behindbearstatue (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_behindbearstatue" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_behindbearstatue", "trq_behindbearstatue"); + +/*QUAKED trigger_multiple_bcs_mp_trq_behindcar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_behindcar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_behindcar", "trq_behindcar"); + +/*QUAKED trigger_multiple_bcs_mp_trq_incoffeeshop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_incoffeeshop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_incoffeeshop", "trq_incoffeeshop"); + +/*QUAKED trigger_multiple_bcs_mp_trq_inconstructionblgd (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_inconstructionblgd" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_inconstructionblgd", "trq_inconstructionblgd"); + +/*QUAKED trigger_multiple_bcs_mp_trq_inelevator (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_inelevator" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_inelevator", "trq_inelevator"); + +/*QUAKED trigger_multiple_bcs_mp_trq_onroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_onroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_onroof", "trq_onroof"); + +/*QUAKED trigger_multiple_bcs_mp_trq_behindsecuritytruck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_behindsecuritytruck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_behindsecuritytruck", "trq_behindsecuritytruck"); + +/*QUAKED trigger_multiple_bcs_mp_trq_aboveticketcounter (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="trq_aboveticketcounter" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_trq_aboveticketcounter", "trq_aboveticketcounter"); +} + +mp_venus() +{ +/*QUAKED trigger_multiple_bcs_mp_vns_inpool (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inpool" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inpool", "vns_inpool"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bypool (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bypool" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bypool", "vns_bypool"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inlounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inlounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inlounge", "vns_inlounge"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inlobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inlobby" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inlobby", "vns_inlobby"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onbalcony (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onbalcony" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onbalcony", "vns_onbalcony"); + +/*QUAKED trigger_multiple_bcs_mp_vns_byhelipad (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_byhelipad" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_byhelipad", "vns_byhelipad"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onfountain (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onfountain" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onfountain", "vns_onfountain"); + +/*QUAKED trigger_multiple_bcs_mp_vns_byfountain (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_byfountain" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_byfountain", "vns_byfountain"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onpoolshade (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onpoolshade" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onpoolshade", "vns_onpoolshade"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inzengarden (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inzengarden" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inzengarden", "vns_inzengarden"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inbar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inbar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inbar", "vns_inbar"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bybar (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bybar" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bybar", "vns_bybar"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onwoodendeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onwoodendeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onwoodendeck", "vns_onwoodendeck"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inobservationlounge (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inobservationlounge" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inobservationlounge", "vns_inobservationlounge"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bywaterfall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bywaterfall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bywaterfall", "vns_bywaterfall"); + +/*QUAKED trigger_multiple_bcs_mp_vns_incentercourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_incentercourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_incentercourtyard", "vns_incentercourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onpatio (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onpatio" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onpatio", "vns_onpatio"); + +/*QUAKED trigger_multiple_bcs_mp_vns_byfallentree (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_byfallentree" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_byfallentree", "vns_byfallentree"); + +/*QUAKED trigger_multiple_bcs_mp_vns_inbansaigarden (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_inbansaigarden" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_inbansaigarden", "vns_inbansaigarden"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bypenthouse (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bypenthouse" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bypenthouse", "vns_bypenthouse"); + +/*QUAKED trigger_multiple_bcs_mp_vns_byhorsestatue (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_byhorsestatue" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_byhorsestatue", "vns_byhorsestatue"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bysoliderstatue (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bysoliderstatue" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bysoliderstatue", "vns_bysoliderstatue"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onhallwayrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onhallwayrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onhallwayrooftop", "vns_onhallwayrooftop"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onbalconystaircase (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onbalconystaircase" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onbalconystaircase", "vns_onbalconystaircase"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onoutsidebalconystaircase (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onoutsidebalconystaircase" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onoutsidebalconystaircase", "vns_onoutsidebalconystaircase"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onobservationdeck (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onobservationdeck" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onobservationdeck", "vns_onobservationdeck"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onobservationrooftop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onobservationrooftop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onobservationrooftop", "vns_onobservationrooftop"); + +/*QUAKED trigger_multiple_bcs_mp_vns_underwaterfall (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_underwaterfall" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_underwaterfall", "vns_underwaterfall"); + +/*QUAKED trigger_multiple_bcs_mp_vns_insaunalobby (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_insaunalobby" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_insaunalobby", "vns_insaunalobby"); + +/*QUAKED trigger_multiple_bcs_mp_vns_underkeyholearchway (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_underkeyholearchway" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_underkeyholearchway", "vns_underkeyholearchway"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onoverhang (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onoverhang" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onoverhang", "vns_onoverhang"); + +/*QUAKED trigger_multiple_bcs_mp_vns_bylanterns (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_bylanterns" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_bylanterns", "vns_bylanterns"); + +/*QUAKED trigger_multiple_bcs_mp_vns_onpenthouseroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="vns_onpenthouseroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_vns_onpenthouseroof", "vns_onpenthouseroof"); +} + +mp_comeback() +{ +/*QUAKED trigger_multiple_bcs_mp_cbk_rearentrancenetcafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_rearentrancenetcafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_rearentrancenetcafe", "cbk_rearentrancenetcafe"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_netcafe (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_netcafe" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_netcafe", "cbk_netcafe"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_videostore (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_videostore" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_videostore", "cbk_videostore"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_videostoreroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_videostoreroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_videostoreroof", "cbk_videostoreroof"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_byshanty (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_byshanty" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_byshanty", "cbk_byshanty"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_powerstationroof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_powerstationroof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_powerstationroof", "cbk_powerstationroof"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_centercoil (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_centercoil" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_centercoil", "cbk_centercoil"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_glassoffices (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_glassoffices" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_glassoffices", "cbk_glassoffices"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_grassybalcony (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_grassybalcony" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_grassybalcony", "cbk_grassybalcony"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_fabricsshop (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_fabricsshop" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_fabricsshop", "cbk_fabricsshop"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_fabricsshoproof (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_fabricsshoproof" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_fabricsshoproof", "cbk_fabricsshoproof"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_westcourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_westcourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_westcourtyard", "cbk_westcourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_nearskywalk (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_nearskywalk" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_nearskywalk", "cbk_nearskywalk"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_baseofapttower (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_baseofapttower" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_baseofapttower", "cbk_baseofapttower"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_northcourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_northcourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_northcourtyard", "cbk_northcourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_atopensewer (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_atopensewer" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_atopensewer", "cbk_atopensewer"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_eastcourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_eastcourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_eastcourtyard", "cbk_eastcourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_nearmarket (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_nearmarket" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_nearmarket", "cbk_nearmarket"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_oneonezero (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_oneonezero" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_oneonezero", "cbk_oneonezero"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_southcourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_southcourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_southcourtyard", "cbk_southcourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_southerncourtyard (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_southerncourtyard" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_southerncourtyard", "cbk_southerncourtyard"); + +/*QUAKED trigger_multiple_bcs_mp_cbk_outsideofficebldg (0 0.25 0.5)? +defaulttexture="bcs" +soundalias="cbk_outsideofficebldg" +*/ + + add_bcs_location_mapping( "trigger_multiple_bcs_mp_cbk_outsideofficebldg", "cbk_outsideofficebldg"); +} + +//--------------------------------------------------------- +// DELETE THESE +//--------------------------------------------------------- +old_locations() +{ +/*QUAKED trigger_multiple_bcs_ns_acrosschasm (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="acrosschasm" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_acrosschasm", "acrosschasm" ); + +/*QUAKED trigger_multiple_bcs_ns_amcrt_stck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="amcrt_stck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_amcrt_stck", "amcrt_stck" ); + +/*QUAKED trigger_multiple_bcs_ns_barr_conc (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="barr_conc" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_barr_conc", "barr_conc" ); + +/*QUAKED trigger_multiple_bcs_ns_brls (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="brls" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_brls", "brls" ); + +/*QUAKED trigger_multiple_bcs_ns_catwlk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="catwlk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_catwlk", "catwlk" ); + +/*QUAKED trigger_multiple_bcs_ns_cell_l (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cell_l" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cell_l", "cell_l" ); + +/*QUAKED trigger_multiple_bcs_ns_cell_r (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cell_r" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cell_r", "cell_r" ); + +/*QUAKED trigger_multiple_bcs_ns_celldr_endhl (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="celldr_endhl" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_celldr_endhl", "celldr_endhl" ); + +/*QUAKED trigger_multiple_bcs_ns_corrgatedmtl (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="corrgatedmtl" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_corrgatedmtl", "corrgatedmtl" ); + +/*QUAKED trigger_multiple_bcs_ns_cot (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cot" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cot", "cot" ); + +/*QUAKED trigger_multiple_bcs_ns_crt_stck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="crt_stck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_crt_stck", "crt_stck" ); + +/*QUAKED trigger_multiple_bcs_ns_crtstk_nrldge (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="crtstk_nrldge" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_crtstk_nrldge", "crtstk_nrldge" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_cent (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_cent" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_cent", "cv_cent" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_cent_concsup (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_cent_concsup" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_cent_concsup", "cv_cent_concsup" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_cent_tv (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_cent_tv" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_cent_tv", "cv_cent_tv" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_small_l (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_small_l" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_small_l", "cv_small_l" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_wall_inside (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_wall_inside" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_wall_inside", "cv_wall_inside" ); + +/*QUAKED trigger_multiple_bcs_ns_cv_wall_outside (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cv_wall_outside" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_cv_wall_outside", "cv_wall_outside" ); + +/*QUAKED trigger_multiple_bcs_ns_dpstr (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="dpstr" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_dpstr", "dpstr" ); + +/*QUAKED trigger_multiple_bcs_ns_drvwy (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="drvwy" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_drvwy", "drvwy" ); + +/*QUAKED trigger_multiple_bcs_ns_dsk_lg (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="dsk_lg" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_dsk_lg", "dsk_lg" ); + +/*QUAKED trigger_multiple_bcs_ns_dsk_stck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="dsk_stck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_dsk_stck", "dsk_stck" ); + +/*QUAKED trigger_multiple_bcs_ns_fuelcont (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fuelcont" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_fuelcont", "fuelcont" ); + +/*QUAKED trigger_multiple_bcs_ns_fuelconts (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fuelconts" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_fuelconts", "fuelconts" ); + +/*QUAKED trigger_multiple_bcs_ns_gbgcns (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="gbgcns" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_gbgcns", "gbgcns" ); + +/*QUAKED trigger_multiple_bcs_ns_hdghog (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hdghog" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_hdghog", "hdghog" ); + +/*QUAKED trigger_multiple_bcs_ns_hesco_nrledge (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hesco_nrledge" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_hesco_nrledge", "hesco_nrledge" ); + +/*QUAKED trigger_multiple_bcs_ns_hescobarr (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hescobarr" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_hescobarr", "hescobarr" ); + +/*QUAKED trigger_multiple_bcs_ns_icemach (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="icemach" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_icemach", "icemach" ); + +/*QUAKED trigger_multiple_bcs_ns_intsec_3w (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="intsec_3w" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_intsec_3w", "intsec_3w" ); + +/*QUAKED trigger_multiple_bcs_ns_lckr_cntr (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lckr_cntr" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lckr_cntr", "lckr_cntr" ); + +/*QUAKED trigger_multiple_bcs_ns_lckr_l (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lckr_l" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lckr_l", "lckr_l" ); + +/*QUAKED trigger_multiple_bcs_ns_lckr_ne (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lckr_ne" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lckr_ne", "lckr_ne" ); + +/*QUAKED trigger_multiple_bcs_ns_lckr_r (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lckr_r" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lckr_r", "lckr_r" ); + +/*QUAKED trigger_multiple_bcs_ns_lckr_sw (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lckr_sw" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lckr_sw", "lckr_sw" ); + +/*QUAKED trigger_multiple_bcs_ns_lowwall_bwire (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="lowwall_bwire" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_lowwall_bwire", "lowwall_bwire" ); + +/*QUAKED trigger_multiple_bcs_ns_newsbox (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="newsbox" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_newsbox", "newsbox" ); + +/*QUAKED trigger_multiple_bcs_ns_phnbth (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="phnbth" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_phnbth", "phnbth" ); + +/*QUAKED trigger_multiple_bcs_ns_pipes_behind (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="pipes_behind" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_pipes_behind", "pipes_behind" ); + +/*QUAKED trigger_multiple_bcs_ns_pipes_nside (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="pipes_nside" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_pipes_nside", "pipes_nside" ); + +/*QUAKED trigger_multiple_bcs_ns_rappel_left (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rappel_left" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_rappel_left", "rappel_left" ); + +/*QUAKED trigger_multiple_bcs_ns_samlnchr (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="samlnchr" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_samlnchr", "samlnchr" ); + +/*QUAKED trigger_multiple_bcs_ns_sentrygun (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sentrygun" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_sentrygun", "sentrygun" ); + +/*QUAKED trigger_multiple_bcs_ns_shwr_cntr (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="shwr_cntr" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_shwr_cntr", "shwr_cntr" ); + +/*QUAKED trigger_multiple_bcs_ns_shwr_ne (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="shwr_ne" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_shwr_ne", "shwr_ne" ); + +/*QUAKED trigger_multiple_bcs_ns_shwr_sw (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="shwr_sw" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_shwr_sw", "shwr_sw" ); + +/*QUAKED trigger_multiple_bcs_ns_sndbgs (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="sndbgs" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_sndbgs", "sndbgs" ); + +/*QUAKED trigger_multiple_bcs_ns_stairs_down (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stairs_down" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_stairs_down", "stairs_down" ); + +/*QUAKED trigger_multiple_bcs_ns_stairs_up (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stairs_up" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_stairs_up", "stairs_up" ); + +/*QUAKED trigger_multiple_bcs_ns_stairs_ylw (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stairs_ylw" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_stairs_ylw", "stairs_ylw" ); + +/*QUAKED trigger_multiple_bcs_ns_tun_leadoutside (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="tun_leadoutside" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_tun_leadoutside", "tun_leadoutside" ); + +/*QUAKED trigger_multiple_bcs_ns_vendmach (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="vendmach" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_vendmach", "vendmach" ); + +/*QUAKED trigger_multiple_bcs_ns_wirespl_lg (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="wirespl_lg" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_wirespl_lg", "wirespl_lg" ); + +/*QUAKED trigger_multiple_bcs_ns_wlkwy_abv_archs (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="wlkwy_abv_archs" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_ns_wlkwy_abv_archs", "wlkwy_abv_archs" ); + +/*QUAKED trigger_multiple_bcs_df_monument_courtyard (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="monument_courtyard" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_monument_courtyard", "monument_courtyard" ); + +/*QUAKED trigger_multiple_bcs_df_monument_top (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="monument_top" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_monument_top", "monument_top" ); + +/*QUAKED trigger_multiple_bcs_df_car_parked (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="car_parked" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_car_parked", "car_parked" ); + +/*QUAKED trigger_multiple_bcs_df_embassy (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_embassy", "embassy" ); + +/*QUAKED trigger_multiple_bcs_df_embassy_1st (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_1st" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_embassy_1st", "embassy_1st" ); + +/*QUAKED trigger_multiple_bcs_df_embassy_3rd (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="embassy_3rd" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_embassy_3rd", "embassy_3rd" ); + +/*QUAKED trigger_multiple_bcs_df_vehicle_snowcat (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="vehicle_snowcat" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_vehicle_snowcat", "vehicle_snowcat" ); + +/*QUAKED trigger_multiple_bcs_df_vehicle_dumptruck (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="vehicle_dumptruck" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_vehicle_dumptruck", "vehicle_dumptruck" ); + +/*QUAKED trigger_multiple_bcs_df_building_red (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="building_red" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_building_red", "building_red" ); + +/*QUAKED trigger_multiple_bcs_df_vehicle_snowmobile (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="vehicle_snowmobile" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_vehicle_snowmobile", "vehicle_snowmobile" ); + +/*QUAKED trigger_multiple_bcs_df_scaffolding_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="scaffolding_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_scaffolding_generic", "scaffolding_generic" ); + +/*QUAKED trigger_multiple_bcs_df_container_red (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="container_red" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_container_red", "container_red" ); + +/*QUAKED trigger_multiple_bcs_df_tires_large (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="tires_large" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_tires_large", "tires_large" ); + +/*QUAKED trigger_multiple_bcs_df_memorial_building (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="memorial_building" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_memorial_building", "memorial_building" ); + +/*QUAKED trigger_multiple_bcs_df_stand_hotdog (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stand_hotdog" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_stand_hotdog", "stand_hotdog" ); + +/*QUAKED trigger_multiple_bcs_df_stand_trading (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stand_trading" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_stand_trading", "stand_trading" ); + +/*QUAKED trigger_multiple_bcs_df_subway_entrance (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="subway_entrance" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_subway_entrance", "subway_entrance" ); + +/*QUAKED trigger_multiple_bcs_df_rubble_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="rubble_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_rubble_generic", "rubble_generic" ); + +/*QUAKED trigger_multiple_bcs_df_cases_right (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cases_right" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_cases_right", "cases_right" ); + +/*QUAKED trigger_multiple_bcs_df_cases_left (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cases_left" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_cases_left", "cases_left" ); + +/*QUAKED trigger_multiple_bcs_df_cases_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="cases_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_cases_generic", "cases_generic" ); + +/*QUAKED trigger_multiple_bcs_df_barrier_orange (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="barrier_orange" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_barrier_orange", "barrier_orange" ); + +/*QUAKED trigger_multiple_bcs_df_barrier_hesco (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="barrier_hesco" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_barrier_hesco", "barrier_hesco" ); + +/*QUAKED trigger_multiple_bcs_df_stryker_destroyed (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="stryker_destroyed" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_stryker_destroyed", "stryker_destroyed" ); + +/*QUAKED trigger_multiple_bcs_df_fan_exhaust (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="fan_exhaust" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_fan_exhaust", "fan_exhaust" ); + +/*QUAKED trigger_multiple_bcs_df_tower_jamming (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="tower_jamming" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_tower_jamming", "tower_jamming" ); + +/*QUAKED trigger_multiple_bcs_df_ac_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="ac_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_ac_generic", "ac_generic" ); + +/*QUAKED trigger_multiple_bcs_df_table_computer (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="table_computer" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_table_computer", "table_computer" ); + +/*QUAKED trigger_multiple_bcs_df_bulkhead_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bulkhead_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_bulkhead_generic", "bulkhead_generic" ); + +/*QUAKED trigger_multiple_bcs_df_bunk_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bunk_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_bunk_generic", "bunk_generic" ); + +/*QUAKED trigger_multiple_bcs_df_console_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="console_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_console_generic", "console_generic" ); + +/*QUAKED trigger_multiple_bcs_df_deck_generic (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="deck_generic" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_df_deck_generic", "deck_generic" ); + + +} + +old_locations_mp() +{ +/*QUAKED trigger_multiple_bcs_mp_dome_bunker (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bunker" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_bunker", "bunker" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_bunker_back (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="bunker_back" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_bunker_back", "bunker_back" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_office (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="office" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_office", "office" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_dome (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="dome" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_dome", "dome" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_catwalk (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="catwalk" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_catwalk", "catwalk" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_loadingbay (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="loadingbay" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_loadingbay", "loadingbay" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_hallway (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hallway" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_hallway", "hallway" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_hallway_loadingbay (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hallway_loadingbay" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_hallway_loadingbay", "hallway_loadingbay" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_hallway_office (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="hallway_office" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_hallway_office", "hallway_office" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_wall_broken (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="wall_broken" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_wall_broken", "wall_broken" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_tank (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="tank" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_tank", "tank" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_radar (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="radar" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_radar", "radar" ); + +/*QUAKED trigger_multiple_bcs_mp_dome_humvee (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="humvee" +*/ + add_bcs_location_mapping( "trigger_multiple_bcs_mp_dome_humvee", "humvee" ); +} + +/*EXAMPLEQUAKED trigger_multiple_bcs_df_parisAC130_lm_embassy (0 0.25 0.5) ? +defaulttexture="bcs" +soundalias="DF_1_lm_embassy" +*/ +// add_bcs_location_mapping( "trigger_multiple_bcs_df_parisAC130_lm_embassy", "lm_embassy" ); diff --git a/raw/common_scripts/_createfx.gsc b/raw/common_scripts/_createfx.gsc new file mode 100644 index 0000000..0e262c5 --- /dev/null +++ b/raw/common_scripts/_createfx.gsc @@ -0,0 +1,3166 @@ +#include common_scripts\utility; +#include common_scripts\_fx; +#include common_scripts\_createfxMenu; +#include common_scripts\_exploder; + +SoundOnly() +{ + return GetDvar( "scr_createfx_type", "0" ) == "2"; +} + +FxOnly() +{ + return GetDvar( "scr_createfx_type", "0" ) == "1"; +} + +TrackNonEditFx( ent ) +{ + if ( isdefined( level.tracked_ent ) ) + { + if ( !isdefined( level.tracked_ents ) ) + level.tracked_ents = []; + level.tracked_ents[level.tracked_ents.size] = level.tracked_ent.v; + } + level.tracked_ent = ent; +} + +createEffect( type, fxid ) +{ + ent = spawnStruct(); + if ( SoundOnly() ) + { + TrackNonEditFx( ent ); + } + else + { + if ( !IsDefined( level.createFXent ) ) + { + level.createFXent = []; + } + + level.createFXent[ level.createFXent.size ] = ent; + } + ent.v = []; + ent.v[ "type" ] = type; + ent.v[ "fxid" ] = fxid; + ent.v[ "angles" ] = ( 0, 0, 0 ); + ent.v[ "origin" ] = ( 0, 0, 0 ); + ent.drawn = true; + + if ( IsDefined( fxid ) && IsDefined( level.createFXbyFXID ) ) + { // if we're using the optimized lookup, add it in the proper place + ary = level.createFXbyFXID[ fxid ]; + if ( !IsDefined( ary ) ) + { + ary = []; + } + + ary[ ary.size ] = ent; + level.createFXbyFXID[ fxid ] = ary; + } + return ent; +} + +// the following "get" functions are used to centralize the default values for some of the createFX options. +// This is so that we don't have to write the options into the createfx file if they are the same as the default. + +getLoopEffectDelayDefault() +{ + return 0.5; +} + +getOneshotEffectDelayDefault() +{ + return -15; +} + +getExploderDelayDefault() +{ + return 0; +} + +getIntervalSoundDelayMinDefault() +{ + return .75; +} + +getIntervalSoundDelayMaxDefault() +{ + return 2; +} + +createLoopSound() +{ + ent = spawnStruct(); + if ( FxOnly() ) + { + TrackNonEditFx( ent ); + } + else + { + if ( !IsDefined( level.createFXent ) ) + level.createFXent = []; + + level.createFXent[ level.createFXent.size ] = ent; + } + ent.v = []; + ent.v[ "type" ] = "soundfx"; + ent.v[ "fxid" ] = "No FX"; + ent.v[ "soundalias" ] = "nil"; + ent.v[ "angles" ] = ( 0, 0, 0 ); + ent.v[ "origin" ] = ( 0, 0, 0 ); + ent.v[ "server_culled" ] = 1; + if ( getdvar( "serverCulledSounds" ) != "1" ) + ent.v[ "server_culled" ] = 0; + ent.drawn = true; + return ent; +} + +createIntervalSound() +{ + ent = createLoopSound(); + ent.v[ "type" ] = "soundfx_interval"; + ent.v[ "delay_min" ] = getIntervalSoundDelayMinDefault(); + ent.v[ "delay_max" ] = getIntervalSoundDelayMaxDefault(); + return ent; +} + +createDynamicAmbience() +{ + ent = spawnStruct(); + if ( FxOnly() ) + { + TrackNonEditFx( ent ); + } + else + { + if ( !IsDefined( level.createFXent ) ) + level.createFXent = []; + + level.createFXent[ level.createFXent.size ] = ent; + } + ent.v = []; + ent.v[ "origin" ] = ( 0, 0, 0 ); + ent.v[ "dynamic_distance" ] = 1000; + ent.v[ "fxid" ] = "No FX"; + ent.v[ "type" ] = "soundfx_dynamic"; + ent.v[ "ambiencename" ] = "nil"; + + return ent; +} + +createNewExploder() +{ + ent = spawnStruct(); + if ( FxOnly() ) + { + TrackNonEditFx( ent ); + } + else + { + if ( !IsDefined( level.createFXent ) ) + level.createFXent = []; + + level.createFXent[ level.createFXent.size ] = ent; + } + ent.v = []; + ent.v[ "type" ] = "exploder"; + ent.v[ "fxid" ] = "No FX"; + ent.v[ "soundalias" ] = "nil"; + ent.v[ "loopsound" ] = "nil"; + ent.v[ "angles" ] = ( 0, 0, 0 ); + ent.v[ "origin" ] = ( 0, 0, 0 ); + ent.v[ "exploder" ] = 1; + ent.v[ "flag" ] = "nil"; + ent.v[ "exploder_type" ] = "normal"; + ent.drawn = true; + return ent; +} + +createExploderEx( fxid, exploderID ) +{ + ent = createExploder( fxid ); + ent.v[ "exploder" ] = exploderID; + return ent; +} + +createReactiveEnt() +{ + ent = spawnStruct(); + if ( SoundOnly() ) + { + TrackNonEditFx( ent ); + } + else + { + if ( !IsDefined( level.createFXent ) ) + level.createFXent = []; + + level.createFXent[ level.createFXent.size ] = ent; + } + ent.v = []; + ent.v[ "origin" ] = ( 0, 0, 0 ); + ent.v[ "reactive_radius" ] = 200; + ent.v[ "fxid" ] = "No FX"; + ent.v[ "type" ] = "reactive_fx"; + ent.v[ "soundalias" ] = "nil"; + + return ent; +} + +set_origin_and_angles( origin, angles ) +{ + // Use this if your level mapfile is offset in the main map. + if( isDefined( level.createFX_offset )) + { + origin = ( origin + level.createFX_offset ); + } + self.v[ "origin" ] = origin; + self.v[ "angles" ] = angles; +} + +set_forward_and_up_vectors() +{ + self.v[ "up" ] = AnglesToUp( self.v[ "angles" ] ); + self.v[ "forward" ] = AnglesToForward( self.v[ "angles" ] ); +} + +convertOneShotFx() +{ + + + setDvarIfUninitialized( "curr_exp_num", 1 ); + + + expNum = getdvarint( "curr_exp_num" ); + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + SetFXKillOnDelete(ent.looper, true); + waitframe(); + ent pauseEffect(); //turn off old oneshot + ent.v[ "type" ] = "exploder"; + ent.v[ "exploder" ] = expNum; + ent.v[ "exploder_type" ] = "normal"; + ent activate_individual_exploder(); + } + level._createfx.justConvertedOneshot = 1; +} + +createfx_common() +{ + precacheShader( "black" ); + + level._createfx = SpawnStruct(); + level._createfx.grenade = Spawn( "script_origin", ( 0, 0, 0 ) ); + level._createfx.grenade.fx = LoadFX("vfx/explosion/frag_grenade_default"); + level._createfx.grenade.sound = "null"; + level._createfx.grenade.radius = 256; + + if ( level.mp_createfx ) + { + hack_start( "painter_mp" ); + } + else + { + hack_start( "painter" ); + } + + flag_init( "createfx_saving" ); + flag_init( "createfx_started" ); + + // Effects placing tool + if ( !IsDefined( level.createFX ) ) + { + level.createFX = []; + } + + level.createfx_loopcounter = 0; + +// NOT MP FRIENDLY +// array_call( GetSpawnerArray(), ::Delete ); +// +// ents = GetEntArray( "trigger_multiple", "code_classname" ); +// ents = array_combine( ents, GetEntArray( "trigger_radius", "code_classname" ) ); +// ents = array_combine( ents, GetEntArray( "trigger_once", "code_classname" ) ); + +// array_call( ents, ::Delete ); + + SetDvar("ui_hidehud", "1"); + level notify( "createfx_common_done" ); +} + +//--------------------------------------------------------- +// CreateFx Logic Initialization Section +//--------------------------------------------------------- +init_level_variables() +{ + // gets cumulatively added to to create digital accelleration + level._createfx.selectedMove_up = 0; + level._createfx.selectedMove_forward = 0; + level._createfx.selectedMove_right = 0; + level._createfx.selectedRotate_pitch = 0; + level._createfx.selectedRotate_roll = 0; + level._createfx.selectedRotate_yaw = 0; + level._createfx.selected_fx = []; + level._createfx.selected_fx_ents = []; + level._createfx.justConvertedOneshot = 0; + + // Selected Ent Settings + level._createfx.rate = 1; + level._createfx.snap2normal = 0; + level._createfx.snap90deg = 0; + level._createfx.localrot = 0; + level._createfx.axismode = 0; + + // Select By Name + level._createfx.select_by_name = false; // unhighlights current ent + + level._createfx.player_speed = GetDvarFloat( "g_speed" ); + set_player_speed_hud(); + +} + +init_locked_list() +{ + level._createfx.lockedList = []; + level._createfx.lockedList[ "escape" ] = true; + level._createfx.lockedList[ "BUTTON_LSHLDR" ] = true; + level._createfx.lockedList[ "BUTTON_RSHLDR" ] = true; + level._createfx.lockedList[ "mouse1" ] = true; + level._createfx.lockedList[ "ctrl" ] = true; +} + +init_colors() +{ + colors = []; + colors[ "loopfx" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "loopfx" ][ "highlighted" ] = ( 0.4, 0.95, 1.0 ); + colors[ "loopfx" ][ "default" ] = ( 0.3, 0.8, 1.0 ); + + colors[ "oneshotfx" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "oneshotfx" ][ "highlighted" ] = ( 0.3, 0.6, 1.0 ); + colors[ "oneshotfx" ][ "default" ] = ( 0.1, 0.2, 1.0 ); + + colors[ "exploder" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "exploder" ][ "highlighted" ] = ( 1.0, 0.2, 0.2 ); + colors[ "exploder" ][ "default" ] = ( 1.0, 0.1, 0.1 ); + + colors[ "rainfx" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "rainfx" ][ "highlighted" ] = ( .95, 0.4, 0.95 ); + colors[ "rainfx" ][ "default" ] = ( .78, 0.0, 0.73 ); + + colors[ "soundfx" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "soundfx" ][ "highlighted" ] = ( .2, 1.0, 0.2 ); + colors[ "soundfx" ][ "default" ] = ( 0.1, 1.0, 0.1 ); + + colors[ "soundfx_interval" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "soundfx_interval" ][ "highlighted" ] = ( .3, 1.0, 0.3 ); + colors[ "soundfx_interval" ][ "default" ] = ( .1, 1.0, 0.1 ); + + colors[ "reactive_fx" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "reactive_fx" ][ "highlighted" ] = ( .5, 1.0, 0.75 ); + colors[ "reactive_fx" ][ "default" ] = ( .2, 0.9, 0.2 ); + + colors[ "soundfx_dynamic" ][ "selected" ] = ( 1.0, 1.0, 0.2 ); + colors[ "soundfx_dynamic" ][ "highlighted" ] = ( .3, 1.0, 0.3 ); + colors[ "soundfx_dynamic" ][ "default" ] = ( .1, 1.0, 0.1 ); + + level._createfx.colors = colors; +} + +//--------------------------------------------------------- +// CreateFx Logic Section +//--------------------------------------------------------- +createFxLogic() +{ + waittillframeend;// let _load run first + wait( 0.05 ); + + if ( !IsDefined( level._effect ) ) + { + level._effect = []; + } + + if ( GetDvar( "createfx_map" ) == "" ) + { + SetDevDvar( "createfx_map", get_template_level() ); + } + else if ( GetDvar( "createfx_map" ) == get_template_level() ) + { + [[ level.func_position_player ]](); + } + + init_crosshair(); + init_menu(); + init_huds(); + init_tool_hud(); + init_crosshair(); + init_level_variables(); + init_locked_list(); + init_colors(); + + SetDevDvar( "fx", "nil" ); + SetDevDvar( "select_by_substring", "" ); + + if ( GetDvar( "createfx_use_f4" ) == "" ) + SetDevDvar( "createfx_use_f4", "0" ); + + if ( GetDvar( "createfx_no_autosave" ) == "" ) + SetDevDvar( "createfx_no_autosave", "0" ); + + level.createfx_draw_enabled = true; + level.last_displayed_ent = undefined; + + level.buttonIsHeld = []; + lastPlayerOrigin = (0,0,0); + + flag_set( "createfx_started" ); + if ( !level.mp_createfx ) + { + lastPlayerOrigin = level.player.origin; + } + + lastHighlightedEnt = undefined; + level.fx_rotating = false; + setMenu( "none" ); + level.createfx_selecting = false; + + // black background for text + black = newHudElem(); + black.x = -120; + black.y = 200; + black.foreground = 0; + black setShader( "black", 250, 160 ); + black.alpha = 0;// 0.6; + + level.createfx_inputlocked = false; + + foreach ( ent in level.createFXent ) + { + ent post_entity_creation_function(); + } + + thread draw_distance(); + lastSelectEntity = undefined; + thread createfx_autosave(); + level.createfx_last_movement_timer = 0; + thread save_undo_buffer(); + thread setup_last_movement_timer(); + + for ( ;; ) + { + changedSelectedEnts = false; + + // calculate the "cursor" + right = anglestoright( level.player getplayerangles() ); + forward = anglestoforward( level.player getplayerangles() ); + up = anglestoup( level.player getplayerangles() ); + dot = 0.85; + + placeEnt_vector = ( forward * 750 ); + level.createfxCursor = bullettrace( level.player geteye(), level.player geteye() + placeEnt_vector, false, undefined ); + highlightedEnt = undefined; + + // ************************************************************ + // + // General input + // + // ************************************************************ + + level.buttonClick = []; + level.button_is_kb = []; + process_button_held_and_clicked(); + ctrlHeld = button_is_held( "ctrl", "BUTTON_LSHLDR" ); + leftClick = button_is_clicked( "mouse1", "BUTTON_A" ); + leftHeld = button_is_held( "mouse1", "BUTTON_A" ); + shiftHeld = button_is_held( "shift" ); + + create_fx_menu(); + + //changing to allow devgui item + // FOR PC Debugging purposes + btn = "F5"; + if ( GetDvarInt( "createfx_use_f4" ) ) + { + btn = "F4"; + } + + if ( button_is_clicked( btn ) ) + SetDevDvar( "scr_createfx_dump", 1 ); + + if( GetDvarInt( "scr_createfx_dump" ) ) + { + SetDevDvar( "scr_createfx_dump", 0 ); + generate_fx_log(); + } + + if ( button_is_clicked( "F2" ) ) + toggle_createfx_drawing(); + + if ( button_is_clicked( "ins" ) ) + insert_effect(); + + if ( button_is_clicked( "del" ) ) + delete_pressed(); + + if ( button_is_clicked( "escape" ) ) + clear_settable_fx(); + + if ( button_is_clicked( "rightarrow", "space" ) && !level.createfx_menu_list_active) + set_off_exploders(); + + if ( button_is_clicked( "leftarrow" ) && !level.createfx_menu_list_active ) + turn_off_exploders(); + + if (button_is_clicked("f")) + frame_selected(); + + if ( button_is_clicked( "u" ) ) + select_by_name_list(); + + if ( button_is_clicked( "c" ) ) + convertOneShotFx(); + + + modify_player_speed(); + + if ( !ctrlHeld && button_is_clicked( "g" ) ) + { + select_all_exploders_of_currently_selected( "exploder" ); + select_all_exploders_of_currently_selected( "flag" ); + } + + if ( button_is_clicked( "h", "F1" ) ) + show_help(); + + if ( button_is_clicked( "BUTTON_LSTICK" ) ) + copy_ents(); + if ( button_is_clicked( "BUTTON_RSTICK" ) ) + paste_ents(); + + if ( button_is_clicked( "z" ) ) + undo(); + + if ( button_is_clicked( "z" ) && shiftHeld ) + redo(); + + if ( ctrlHeld ) + { + if ( button_is_clicked( "c" ) ) + copy_ents(); + + if ( button_is_clicked( "v" ) ) + paste_ents(); + + if ( button_is_clicked( "g" ) ) + spawn_grenade(); // for testing reactive ents + } + + if ( isdefined( level._createfx.selected_fx_option_index ) ) + menu_fx_option_set(); + + //------------------------------------------------- + // Highlighted Entity Handling + //------------------------------------------------- + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + + difference = VectorNormalize( ent.v[ "origin" ] - ( level.player.origin + ( 0, 0, 55 ) ) ); + newdot = vectordot( forward, difference ); + if ( newdot < dot ) + { + continue; + } + + dot = newdot; + highlightedEnt = ent; + } + + level.fx_highLightedEnt = highLightedEnt; + + if ( IsDefined( highLightedEnt ) ) + { + if ( IsDefined( lastHighlightedEnt ) ) + { + if ( lastHighlightedEnt != highlightedEnt ) + { + // a highlighted ent is no longer highlighted so scale down the text size +// lastHighlightedEnt.text = "."; +// lastHighlightedEnt.textsize = 2; + if ( !ent_is_selected( lastHighlightedEnt ) ) + lastHighlightedEnt thread entity_highlight_disable(); + + // an ent became highlighted for the first time so scale up the text size on the new ent +// highlightedEnt.text = HighlightedEnt.v["fxid"]; +// highlightedEnt.textsize = 1; + if ( !ent_is_selected( highlightedEnt ) ) + highlightedEnt thread entity_highlight_enable(); + } + } + else + { + // an ent became highlighted for the first time so scale up the text size on the new ent +// HighlightedEnt.text = HighlightedEnt.v["fxid"]; +// HighlightedEnt.textsize = 1; + if ( !ent_is_selected( highlightedEnt ) ) + highlightedEnt thread entity_highlight_enable(); + } + } + + manipulate_createfx_ents( highlightedEnt, leftClick, leftHeld, ctrlHeld, right ); + + //------------------------------------------------- + // Handle Selected Entities + //------------------------------------------------- + changedSelectedEnts = handle_selected_ents( changedSelectedEnts ); + wait( 0.05 ); + + if ( changedSelectedEnts ) + update_selected_entities(); + + if( !level.mp_createfx ) + lastPlayerOrigin = [[ level.func_position_player_get ]]( lastPlayerOrigin ); + + lastHighlightedEnt = highlightedEnt; + + // if the last selected entity changes then reset the options offset + if ( last_selected_entity_has_changed( lastSelectEntity ) ) + { + level.effect_list_offset = 0; + clear_settable_fx(); + setmenu( "none" ); + } + + if ( level._createfx.selected_fx_ents.size ) + lastSelectEntity = level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size - 1 ]; + else + lastSelectEntity = undefined; + } +} + +modify_player_speed() +{ + modify_speed = false; + + ctrl_held = button_is_held( "ctrl" ); + if ( button_is_held( "." ) ) + { + if ( ctrl_held ) + { + if ( level._createfx.player_speed < 190 ) + { + level._createfx.player_speed = 190; + } + else + { + level._createfx.player_speed += 10; + } + } + else + { + level._createfx.player_speed += 5; + } + modify_speed = true; + } + else if ( button_is_held( "," ) ) + { + if ( ctrl_held ) + { + if ( level._createfx.player_speed > 190 ) + { + level._createfx.player_speed = 190; + } + else + { + level._createfx.player_speed -= 10; + } + } + else + { + level._createfx.player_speed -= 5; + } + modify_speed = true; + } + + if ( modify_speed ) + { + level._createfx.player_speed = Clamp( level._createfx.player_speed, 5, 500 ); + + [[ level.func_player_speed ]](); + + set_player_speed_hud(); + } +} + +set_player_speed_hud() +{ + if ( !IsDefined( level._createfx.player_speed_hud ) ) + { + hud = newHudElem(); + hud.alignX = "right"; + hud.foreground = 1; + hud.fontScale = 1.2; + hud.alpha = 1.0; + hud.x = 120; + hud.y = 420; + hud SetDevText( "Camera Speed( ): " ); + + hud_value = newHudElem(); + hud_value.alignX = "left"; + hud_value.foreground = 1; + hud_value.fontScale = 1.2; + hud_value.alpha = 1.0; + hud_value.x = 120; + hud_value.y = 420; + + hud.hud_value = hud_value; + + level._createfx.player_speed_hud = hud; + } + + level._createfx.player_speed_hud.hud_value SetValue( level._createfx.player_speed ); +} + +toggle_createfx_drawing() +{ + level.createfx_draw_enabled = !level.createfx_draw_enabled; +} + +insert_effect() +{ + setMenu( "creation" ); + level.effect_list_offset = 0; + clear_fx_hudElements(); + set_fx_hudElement( "Pick effect type to create:" ); + set_fx_hudElement( "1. One Shot FX" ); + set_fx_hudElement( "2. Looping FX" ); + set_fx_hudElement( "3. Looping sound" ); + set_fx_hudElement( "4. Exploder" ); + set_fx_hudElement( "5. One Shot Sound" ); + set_fx_hudElement( "6. Reactive Sound" ); + set_fx_hudElement( "7. Dynamic Ambience" ); + set_fx_hudElement( "(c) Cancel >" ); + set_fx_hudElement( "(x) Exit >" ); + /* + set_fx_hudElement("Pick an effect:"); + set_fx_hudElement("In the console, type"); + set_fx_hudElement("/fx name"); + set_fx_hudElement("Where name is the name of the sound alias"); + */ +} + +manipulate_createfx_ents( highlightedEnt, leftClick, leftHeld, ctrlHeld, right ) +{ + if ( !level.createfx_draw_enabled ) + return; + + if ( level._createfx.select_by_name ) + { + level._createfx.select_by_name = false; + highlightedEnt = undefined; + } + else if ( select_by_substring() ) + { + highlightedEnt = undefined; + } + + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + + if ( !ent.drawn ) + continue; + + scale = GetDvarFloat( "createfx_scaleid" ); + + if ( IsDefined( highlightedEnt ) && ent == highlightedEnt ) + { + if ( !entities_are_selected() ) + display_fx_info( ent ); + + if ( leftClick ) + { + entWasSelected = index_is_selected( i ); + level.createfx_help_active = false; + level.createfx_selecting = !entWasSelected;// used for drag select / deselect + if ( !ctrlHeld ) + { + selectedSize = level._createfx.selected_fx_ents.size; + clear_entity_selection(); + if ( entWasSelected && selectedSize == 1 ) + select_entity( i, ent ); + } + toggle_entity_selection( i, ent ); + } + else + if ( leftHeld ) + { + if ( ctrlHeld ) + { + if ( level.createfx_selecting ) + select_entity( i, ent ); + + if ( !level.createfx_selecting ) + deselect_entity( i, ent ); + } + } + + colorIndex = "highlighted"; + } + else + { + colorIndex = "default"; + } + + if ( index_is_selected( i ) ) + colorIndex = "selected"; + + ent createfx_print3d( colorIndex, scale, right ); + } +} + +createfx_print3d( colorIndex, scale, right ) +{ + if( GetDvarInt( "fx_showLightGridSampleOffset") != 0 ) + { + offsetscale = GetDvarFloat( "fx_lightGridSampleOffset" ); + forwardoffset = anglestoforward( self.v[ "angles" ] ) * offsetscale; + print3d( self.v[ "origin" ] + forwardoffset , ".", ( 1, 0.411, 0.705 ), 1, scale ); + } + +// Line( level.player.origin, ( 0, 0, 0 ) ); + print3d( self.v[ "origin" ], ".", level._createfx.colors[ self.v[ "type" ] ][ colorIndex ], 1, scale ); + if ( self.textalpha > 0 ) + { + text = self get_print3d_text(); + printRight = ( right * ( text.size * -2.93 ) ); + color = level._createfx.colors[ self.v[ "type" ] ][ colorIndex ]; + + if ( IsDefineD( self.is_playing ) ) + color = ( 1, 0.5, 0 ); + + print3d( self.v[ "origin" ] + printRight + ( 0, 0, 15 ), text, color, self.textalpha, scale ); + + if ( IsDefined( self.v[ "reactive_radius" ] ) ) + { + Sphere( self.v[ "origin" ], self.v[ "reactive_radius" ], color ); + } + else if ( IsDefined( self.v[ "dynamic_distance" ] ) ) + { + Sphere( self.v[ "origin" ], self.v[ "dynamic_distance" ], color ); + } + } +} + +get_print3d_text() +{ + switch ( self.v[ "type" ] ) + { + case "reactive_fx": + return "reactive: " + self.v[ "soundalias" ]; + case "soundfx_interval": + return self.v[ "soundalias" ]; + case "soundfx_dynamic": + return "dynamic: " + self.v[ "ambiencename" ]; + case "soundfx": + return self.v[ "soundalias" ]; + case "exploder": + if (isDefined(self.v[ "soundalias" ] ) && self.v[ "soundalias" ] != "nil") + if (self.v[ "fxid" ] == "No FX") + return "@)) " + self.v[ "soundalias" ]; + else + return self.v[ "fxid" ] + " @))"; + else + return self.v[ "fxid" ]; + case "oneshotfx": + if (isDefined(self.v[ "soundalias" ] ) && self.v[ "soundalias" ] != "nil") + return self.v[ "fxid" ] + " @))"; + else + return self.v[ "fxid" ]; + default: + return self.v[ "fxid" ]; + } +} + +select_by_name_list() +{ + level.effect_list_offset = 0; + clear_fx_hudElements(); + setmenu( "select_by_name" ); + draw_effects_list(); +} + +//--------------------------------------------------------- +// Selected Ent Section +//--------------------------------------------------------- +handle_selected_ents( changedSelectedEnts ) +{ + if ( level._createfx.selected_fx_ents.size > 0 && level.createfx_help_active == false) + { + changedSelectedEnts = selected_ent_buttons( changedSelectedEnts ); + if ( !current_mode_hud( "selected_ents" ) ) + { + new_tool_hud( "selected_ents" ); + //set_tool_hudelem( "Selected Ent Mode" ); + set_tool_hudelem( "Mode:", "move" ); + set_tool_hudelem( "Move Rate( -/+ ):", level._createfx.rate ); + if(level._createfx.snap2normal) + { + snap2normaltext = "on"; + snap2Color = (0,1,0); + } + else + { + snap2normaltext = "off"; + snap2Color = (0.5,0.5,0.5); + } + set_tool_hudelem( "Snap2Normal( S ):", snap2normaltext, snap2Color ); + if(level._createfx.snap90deg) + { + snap90degtext = "on"; + snap90Color = (0,1,0); + } + else + { + snap90degtext = "off"; + snap90Color = (0.5,0.5,0.5); + } + set_tool_hudelem( "90deg Snap( L ):", snap90degtext, snap90Color ); + if(level._createfx.localRot) + { + localRottext = "on"; + localRotColor = (0,1,0); + } + else + { + localRottext = "off"; + localRotColor = (0.5,0.5,0.5); + } + set_tool_hudelem( "Local Rotation( R ):", localRottext, localRotColor ); + + + } + + if ( level._createfx.axismode && level._createfx.selected_fx_ents.size > 0 ) + { + set_tool_hudelem( "Mode:", "rotate" ); + + // draw axis and do rotation + thread [[ level.func_process_fx_rotater ]](); + if ( button_is_clicked( "p" ) ) + reset_axis_of_selected_ents(); + + if ( button_is_clicked( "o" ) ) + aim_axis_of_selected_ents(); + + if ( button_is_clicked( "v" ) ) + copy_angles_of_selected_ents(); + + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + level._createfx.selected_fx_ents[ i ] draw_axis(); + + if ( level.selectedRotate_pitch != 0 || level.selectedRotate_yaw != 0 || level.selectedRotate_roll != 0 ) + changedSelectedEnts = true; + /* + for ( i=0; i < level._createfx.selected_fx_ents.size; i++) + { + ent = level._createfx.selected_fx_ents[i]; + ent.angles = ent.angles + (level.selectedRotate_pitch, level.selectedRotate_yaw, 0); + ent set_forward_and_up_vectors(); + } + + if (level.selectedRotate_pitch != 0 || level.selectedRotate_yaw != 0) + changedSelectedEnts = true; + */ + } + else + { + set_tool_hudelem( "Mode:", "move" ); + selectedMove_vector = get_selected_move_vector(); + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + if ( IsDefined( ent.model ) )// ents with brushmodels are from radiant and dont get moved + continue; + + ent draw_cross(); + ent.v[ "origin" ] = ent.v[ "origin" ] + selectedMove_vector; + } + + if ( distance( ( 0, 0, 0 ), selectedMove_vector ) > 0 ) + { + thread save_undo_buffer(); + level.createfx_last_movement_timer = 0; + changedSelectedEnts = true; + } + } + } + else + { + clear_tool_hud(); + } + + return changedSelectedEnts; +} + +selected_ent_buttons( changedSelectedEnts ) +{ + if ( button_is_clicked( "BUTTON_X" ) ) + { + toggle_axismode(); + } + + modify_rate(); + + if ( button_is_clicked( "s" ) ) + { + toggle_snap2normal(); + } + + if ( button_is_clicked( "l" ) ) + { + toggle_snap90deg(); + } + + if ( button_is_clicked( "r" ) ) + { + toggle_localrot(); + } + + if ( button_is_clicked( "end" ) ) + { + drop_selection_to_ground(); + changedSelectedEnts = true; + } + + if ( button_is_clicked( "tab", "BUTTON_RSHLDR" ) ) + { + move_selection_to_cursor(); + changedSelectedEnts = true; + } + + return changedSelectedEnts; +} + +modify_rate() +{ + shift_held = button_is_held( "shift" ); + ctrl_held = button_is_held( "ctrl" ); + + if ( button_is_clicked( "=" ) ) + { + if ( shift_held ) + { + level._createfx.rate += 0.025; + } + else if ( ctrl_held ) + { + if ( level._createfx.rate < 1 ) + { + level._createfx.rate = 1; + } + else + { + level._createfx.rate += 10; + } + } + else + { + level._createfx.rate += 0.1; + } + + } + else if ( button_is_clicked( "-" ) ) + { + if ( shift_held ) + { + level._createfx.rate -= 0.025; + } + else if ( ctrl_held ) + { + if ( level._createfx.rate > 1 ) + { + level._createfx.rate = 1; + } + else + { + level._createfx.rate = 0.1; + } + } + else + { + level._createfx.rate -= 0.1; + } + } + + level._createfx.rate = Clamp( level._createfx.rate, 0.025, 100 ); + + set_tool_hudelem( "Move Rate( -/+ ):", level._createfx.rate ); +} + +toggle_axismode() +{ + level._createfx.axismode = !level._createfx.axismode; +} + +toggle_snap2normal() +{ + level._createfx.snap2normal = !level._createfx.snap2normal; + if(level._createfx.snap2normal) + { + snap2normaltext = "on"; + snapColor = (0,1,0); + } + else + { + snap2normaltext = "off"; + snapColor = (0.5,0.5,0.5); + } + set_tool_hudelem( "Snap2Normal( S ):", snap2normaltext, snapColor ); +} + +toggle_snap90deg() +{ + level._createfx.snap90deg = !level._createfx.snap90deg; + if(level._createfx.snap90deg) + { + snap90degtext = "on"; + snapColor = (0,1,0); + } + else + { + snap90degtext = "off"; + snapColor = (0.5,0.5,0.5); + } + set_tool_hudelem( "90deg Snap( L ):", snap90degtext, snapColor ); +} + +toggle_localRot() +{ + level._createfx.localRot = !level._createfx.localRot; + if(level._createfx.localRot) + { + localRottext = "on"; + snapColor = (0,1,0); + } + else + { + localRottext = "off"; + snapColor = (0.5,0.5,0.5); + } + set_tool_hudelem( "Local Rotation( R ):", localRottext, snapColor ); +} + +copy_angles_of_selected_ents() +{ + // so it stops rotating them over time + thread save_undo_buffer(); + level notify( "new_ent_selection" ); + + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + ent.v[ "angles" ] = level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size - 1 ].v[ "angles" ]; + ent set_forward_and_up_vectors(); + } + + update_selected_entities(); + level.createfx_last_movement_timer = 0; +} + +aim_axis_of_selected_ents() +{ + // so it stops rotating them over time + thread save_undo_buffer(); + level notify( "new_ent_selection" ); + aim_ent = level._createfx.selected_fx_ents[level._createfx.selected_fx_ents.size - 1]; + for ( i = 0; i < (level._createfx.selected_fx_ents.size - 1); i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + aim_angle = VectorToAngles(aim_ent.v[ "origin" ] - ent.v[ "origin" ]); + ent.v[ "angles" ] = aim_angle; + ent set_forward_and_up_vectors(); + } + + update_selected_entities(); + level.createfx_last_movement_timer = 0; +} + +reset_axis_of_selected_ents() +{ + // so it stops rotating them over time + level notify( "new_ent_selection" ); + thread save_undo_buffer(); + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + ent.v[ "angles" ] = ( 0, 0, 0 ); + ent set_forward_and_up_vectors(); + } + + update_selected_entities(); + level.createfx_last_movement_timer = 0; +} + +last_selected_entity_has_changed( lastSelectEntity ) +{ + if ( IsDefined( lastSelectEntity ) ) + { + if ( !entities_are_selected() ) + return true; + } + else + return entities_are_selected(); + + return( lastSelectEntity != level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size - 1 ] ); +} + +drop_selection_to_ground() +{ + thread save_undo_buffer(); + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + trace = BulletTrace( ent.v[ "origin" ], ent.v[ "origin" ] + ( 0, 0, -2048 ), false, undefined ); + ent.v[ "origin" ] = trace[ "position" ]; + } + level.createfx_last_movement_timer = 0; +} + +set_off_exploders() +{ + level notify( "createfx_exploder_reset" ); + exploders = []; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + if ( IsDefined( ent.v[ "exploder" ] ) ) + exploders[ ent.v[ "exploder" ] ] = true; + } + + keys = getarraykeys( exploders ); + for ( i = 0; i < keys.size; i++ ) + exploder( keys[ i ] ); +} + +turn_off_exploders() +{ + level notify( "createfx_exploder_reset" ); + exploders = []; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + if ( IsDefined( ent.v[ "exploder" ] ) ) + exploders[ ent.v[ "exploder" ] ] = true; + } + + keys = getarraykeys( exploders ); + for ( i = 0; i < keys.size; i++ ) + kill_exploder( keys[ i ] ); +} + +draw_distance() +{ + count = 0; + if ( GetDvarInt( "createfx_drawdist" ) == 0 ) + { + SetDevDvar( "createfx_drawdist", "500" ); + } + + for ( ;; ) + { + maxDist = GetDvarInt( "createfx_drawdist" ); + maxDist *= maxDist; + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + ent.drawn = DistanceSquared( level.player.origin, ent.v[ "origin" ] ) <= maxDist; + + count++ ; + if ( count > 100 ) + { + count = 0; + wait( 0.05 ); + } + } + + if ( level.createFXent.size == 0 ) + { + wait( 0.05 ); + } + } +} + +createfx_autosave() +{ + SetDvarIfUninitialized( "createfx_autosave_time", "300" ); + + for ( ;; ) + { + wait( GetDvarInt( "createfx_autosave_time" ) ); + flag_waitopen( "createfx_saving" ); + + if ( GetDvarInt( "createfx_no_autosave" ) ) + { + continue; + } + + generate_fx_log( true ); + } +} + +rotate_over_time( org, rotater ) +{ + level endon( "new_ent_selection" ); + timer = 0.1; + for ( p = 0; p < timer * 20; p++ ) + { + if ( level.selectedRotate_pitch != 0 ) + org AddPitch( level.selectedRotate_pitch ); + else + //roll and yaw have to be swapped for fx rotation to line up with org rotation + if ( level.selectedRotate_roll != 0 ) + org AddYaw( level.selectedRotate_roll ); + else + org AddRoll( level.selectedRotate_yaw ); + + wait( 0.05 ); + org draw_axis(); + + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + if ( IsDefined( ent.model ) )// ents with brushmodels are from radiant and dont get moved + continue; + + ent.v[ "origin" ] = rotater[ i ].origin; + ent.v[ "angles" ] = rotater[ i ].angles; + } + } +} + +delete_pressed() +{ + //this is never getting called because inputLocked is preventing delete keypress from working + if ( level.createfx_inputlocked ) + { + remove_selected_option(); + return; + } + + delete_selection(); +} + +remove_selected_option() +{ + if ( !IsDefined( level._createfx.selected_fx_option_index ) ) + { + return; + } + + name = level._createfx.options[ level._createfx.selected_fx_option_index ][ "name" ]; + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( !ent_is_selected( ent ) ) + continue; + + ent remove_option( name ); + } + + update_selected_entities(); + clear_settable_fx(); +} + +remove_option( name ) +{ + self.v[ name ] = undefined; +} + +delete_selection() +{ + save_undo_buffer(); + newArray = []; + + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( ent_is_selected( ent ) ) + { + if ( IsDefined( ent.looper ) ) + ent.looper delete(); + + ent notify( "stop_loop" ); + } + else + newArray[ newArray.size ] = ent; + } + + level.createFXent = newArray; + + level._createfx.selected_fx = []; + level._createfx.selected_fx_ents = []; + clear_fx_hudElements(); + save_redo_buffer(); +} + +move_selection_to_cursor() +{ + thread save_undo_buffer(); + origin = level.createfxCursor[ "position" ]; + if ( level._createfx.selected_fx_ents.size <= 0 ) + { + return; + } + + center = get_center_of_array( level._createfx.selected_fx_ents ); + difference = center - origin; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + if ( IsDefined( ent.model ) )// ents with brushmodels are from radiant and dont get moved + { + continue; + } + + ent.v[ "origin" ] -= difference; + + if ( level._createfx.snap2normal ) + { + if ( IsDefined( level.createfxCursor[ "normal" ] ) ) + { + ent.v[ "angles" ] = VectorToAngles( level.createfxCursor[ "normal" ] );; + } + } + } + level.createfx_last_movement_timer = 0; +} + +select_last_entity() +{ + select_entity( level.createFXent.size - 1, level.createFXent[ level.createFXent.size - 1 ] ); +} + +reselect_entitites() +{ + //this works some of the time, not sure why it isn't more reliable + selected_exploders = []; + for ( i = 0; i < level.createFXent.size; i++ ) + { + if (index_is_selected(i)) + { + selected_exploders[selected_exploders.size] = i; + } + } + clear_entity_selection(); + select_index_array(selected_exploders); +} + +select_all_exploders_of_currently_selected( key ) +{ + selected_exploders = []; + foreach ( ent in level._createfx.selected_fx_ents ) + { + if ( !IsDefined( ent.v[ key ] ) ) + continue; + + value = ent.v[ key ]; + selected_exploders[ value ] = true; + } + + foreach ( value, _ in selected_exploders ) + { + foreach ( index, ent in level.createFXent ) + { + if ( index_is_selected( index ) ) + continue; + if ( !IsDefined( ent.v[ key ] ) ) + continue; + if ( ent.v[ key ] != value ) + continue; + + select_entity( index, ent ); + } + } + + update_selected_entities(); +} + +copy_ents() +{ + if ( level._createfx.selected_fx_ents.size <= 0 ) + return; + + array = []; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + newent = spawnstruct(); + + newent.v = ent.v; + newent post_entity_creation_function(); + array[ array.size ] = newent; + } + + level.stored_ents = array; +} + +post_entity_creation_function() +{ + self.textAlpha = 0; + self.drawn = true; +} + +paste_ents() +{ + if ( !IsDefined( level.stored_ents ) ) + return; + + clear_entity_selection(); + + for ( i = 0;i < level.stored_ents.size;i++ ) + add_and_select_entity( level.stored_ents[ i ] ); + + move_selection_to_cursor(); + update_selected_entities(); + level.stored_ents = []; + copy_ents();// roundabout way to put new entities in the copy queue +} + +add_and_select_entity( ent ) +{ + level.createFXent[ level.createFXent.size ] = ent; + select_last_entity(); +} + +get_center_of_array( array ) +{ + center = ( 0, 0, 0 ); + for ( i = 0; i < array.size; i++ ) + center = ( center[ 0 ] + array[ i ].v[ "origin" ][ 0 ], center[ 1 ] + array[ i ].v[ "origin" ][ 1 ], center[ 2 ] + array[ i ].v[ "origin" ][ 2 ] ); + + return( center[ 0 ] / array.size, center[ 1 ] / array.size, center[ 2 ] / array.size ); +} + +get_radius_of_array( array ) +{ + arrayMin = array[ 0 ].v[ "origin" ]; + arrayMax = array[ 0 ].v[ "origin" ]; + + arrayMinX = arrayMin[ 0 ]; + arrayMinY = arrayMin[ 1 ]; + arrayMinZ = arrayMin[ 2 ]; + + arrayMaxX = arrayMax[ 0 ]; + arrayMaxY = arrayMax[ 1 ]; + arrayMaxZ = arrayMax[ 2 ]; + + for ( i = 0; i < array.size; i++ ) + { + entOrigin = array[ i ].v[ "origin" ]; + + if(entOrigin[ 0 ] < arrayMin[ 0 ]) + arrayMinX = entOrigin[ 0 ]; + if(entOrigin[ 0 ] > arrayMax[ 0 ]) + arrayMaxX = entOrigin[ 0 ]; + + if(entOrigin[ 1 ] < arrayMin[ 1 ]) + arrayMinY = entOrigin[ 1 ]; + if(entOrigin[ 1 ] > arrayMax[ 1 ]) + arrayMaxY = entOrigin[ 1 ]; + + if(entOrigin[ 2 ] < arrayMin[ 2 ]) + arrayMinZ = entOrigin[ 2 ]; + if(entOrigin[ 2 ] > arrayMax[ 2 ]) + arrayMaxZ = entOrigin[ 2 ]; + } + arrayMin = (arrayMinX, arrayMinY, arrayMinZ); + arrayMax = (arrayMaxX, arrayMaxY, arrayMaxZ); + camradius = distance(arrayMax,arrayMin); + return camradius; +} + +ent_draw_axis() +{ + self endon( "death" ); + for ( ;; ) + { + draw_axis(); + wait( 0.05 ); + } +} + +rotation_is_occuring() +{ + if ( level.selectedRotate_roll != 0 ) + return true; + if ( level.selectedRotate_pitch != 0 ) + return true; + return level.selectedRotate_yaw != 0; +} + +print_fx_options( ent, tab, autosave ) +{ + for ( i = 0; i < level._createfx.options.size; i++ ) + { + option = level._createfx.options[ i ]; + if ( isdefined( option[ "nowrite" ] ) && option[ "nowrite" ] ) + continue; + optionName = option[ "name" ]; + if ( !IsDefined( ent.v[ optionName ] ) ) + continue; + if ( !mask( option[ "mask" ], ent.v[ "type" ] ) ) + continue; + + if( !level.mp_createfx ) + { + if ( mask( "fx", ent.v[ "type" ] ) && optionName == "fxid" ) + continue; // fxid is already set in the create functions + if ( ent.v[ "type" ] == "exploder" && optionName == "exploder" ) + continue; // exploder is set in createExploderEx() + + key = ent.v[ "type" ] + "/" + optionName; + + if ( IsDefined( level._createfx.defaults[ key ] ) && ( level._createfx.defaults[ key ] == ent.v[ optionName ] ) ) + continue; + } + + if ( option[ "type" ] == "string" ) + { + stringValue = ent.v[ optionName ] + ""; + if ( stringValue == "nil" ) // nil is treated the same as !defined - so why print it out? + continue; + if ( optionName == "platform" && stringValue == "all") + continue; +// if ( !autosave ) +// println( " ent.v[ \"" + optionName + "\" ] = \"" + ent.v[ optionName ] + "\";" ); + cfxprintln( tab + "ent.v[ \"" + optionName + "\" ] = \"" + ent.v[ optionName ] + "\";" ); + continue; + } + + // int or float +// if ( !autosave ) +// println( " ent.v[ \"" + optionName + "\" ] = " + ent.v[ optionName ] + ";" ); + cfxprintln( tab + "ent.v[ \"" + optionName + "\" ] = " + ent.v[ optionName ] + ";" ); + } +} + +entity_highlight_disable() +{ + self notify( "highlight change" ); + self endon( "highlight change" ); + + for ( ;; ) + { + self.textalpha = self.textalpha * 0.85; + self.textalpha = self.textalpha - 0.05; + if ( self.textalpha < 0 ) + break; + wait( 0.05 ); + } + + self.textalpha = 0; +} + +entity_highlight_enable() +{ + self notify( "highlight change" ); + self endon( "highlight change" ); + + for ( ;; ) + { +// self.textalpha = sin(gettime()) * 0.5 + 0.5; + self.textalpha = self.textalpha + 0.05; + self.textalpha = self.textalpha * 1.25; + if ( self.textalpha > 1 ) + break; + wait( 0.05 ); + } + + self.textalpha = 1; + +} + +clear_settable_fx() +{ + level.createfx_inputlocked = false; + SetDevDvar( "fx", "nil" ); + // in case we were modifying an option + level._createfx.selected_fx_option_index = undefined; + reset_fx_hud_colors(); +} + +reset_fx_hud_colors() +{ + for ( i = 0;i < level._createfx.hudelem_count; i++ ) + level._createfx.hudelems[ i ][ 0 ].color = ( 1, 1, 1 ); +} + +toggle_entity_selection( index, ent ) +{ + if ( IsDefined( level._createfx.selected_fx[ index ] ) ) + deselect_entity( index, ent ); + else + select_entity( index, ent ); +} + +select_entity( index, ent ) +{ + if ( IsDefined( level._createfx.selected_fx[ index ] ) ) + return; + clear_settable_fx(); + level notify( "new_ent_selection" ); + + ent thread entity_highlight_enable(); + + level._createfx.selected_fx[ index ] = true; + level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size ] = ent; + + level.createfx_menu_list_active = false; +} + +ent_is_highlighted( ent ) +{ + if ( !IsDefined( level.fx_highLightedEnt ) ) + return false; + return ent == level.fx_highLightedEnt; +} + +deselect_entity( index, ent ) +{ + if ( !IsDefined( level._createfx.selected_fx[ index ] ) ) + return; + + clear_settable_fx(); + level notify( "new_ent_selection" ); + + level._createfx.selected_fx[ index ] = undefined; + + if ( !ent_is_highlighted( ent ) ) + ent thread entity_highlight_disable(); + + // remove the entity from the array of selected entities + newArray = []; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + if ( level._createfx.selected_fx_ents[ i ] != ent ) + newArray[ newArray.size ] = level._createfx.selected_fx_ents[ i ]; + } + level._createfx.selected_fx_ents = newArray; +} + +index_is_selected( index ) +{ + return IsDefined( level._createfx.selected_fx[ index ] ); +} + +ent_is_selected( ent ) +{ + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + if ( level._createfx.selected_fx_ents[ i ] == ent ) + return true; + } + return false; +} + +clear_entity_selection() +{ + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + if ( !ent_is_highlighted( level._createfx.selected_fx_ents[ i ] ) ) + level._createfx.selected_fx_ents[ i ] thread entity_highlight_disable(); + } + level._createfx.selected_fx = []; + level._createfx.selected_fx_ents = []; +} + +draw_axis() +{ +/# + range = 25 * GetDvarFloat( "createfx_scaleid" ); + +// range = 25; + forward = AnglesToForward( self.v[ "angles" ] ); + forward *= range; + right = AnglesToRight( self.v[ "angles" ] ); + right *= range; + up = AnglesToUp( self.v[ "angles" ] ); + up *= range ; + line( self.v[ "origin" ], self.v[ "origin" ] + forward, ( 1, 0, 0 ), 1 ); + print3d(self.v[ "origin" ] + forward, "Y/B", (1,0,0), 1.0, (range / 100)); + line( self.v[ "origin" ], self.v[ "origin" ] + up, ( 0, 1, 0 ), 1 ); + print3d(self.v[ "origin" ] + up, "L/R", (0,1,0), 1.0, (range / 100)); + line( self.v[ "origin" ], self.v[ "origin" ] + right, ( 0, 0, 1 ), 1 ); + print3d(self.v[ "origin" ] + right, "D/U", (0,0,1), 1.0, (range / 100)); +/* + if ( IsDefined( self.v[ "soundalias" ] ) ) + { + DrawSoundShape( self.v[ "origin" ], self.v[ "angles" ], self.v[ "soundalias" ], ( 1, 0, 1 ), 1 ); + } +*/ +#/ +} + +draw_cross() +{ +/# + range = 4; + + Line( self.v[ "origin" ] - ( 0, 0, range ), self.v[ "origin" ] + ( 0, 0, range ) ); + Line( self.v[ "origin" ] - ( 0, range, 0 ), self.v[ "origin" ] + ( 0, range, 0 ) ); + Line( self.v[ "origin" ] - ( range, 0, 0 ), self.v[ "origin" ] + ( range, 0, 0 ) ); +#/ +} + + +createfx_centerprint( text ) +{ + thread createfx_centerprint_thread( text ); +} + +createfx_centerprint_thread( text ) +{ + level notify( "new_createfx_centerprint" ); + level endon( "new_createfx_centerprint" ); + for ( p = 0;p < 5;p++ ) + level.createFX_centerPrint[ p ] setDevText( text ); + wait( 4.5 ); + for ( p = 0;p < 5;p++ ) + level.createFX_centerPrint[ p ] setDevText( "" ); +} + +get_selected_move_vector() +{ + yaw = level.player GetPlayerAngles()[ 1 ]; + angles = ( 0, yaw, 0 ); + right = AnglesToRight( angles ); + forward = AnglesToForward( angles ); + up = AnglesToUp( angles ); + + keypressed = false; + rate = level._createfx.rate; + + if ( buttonDown( "DPAD_UP" ) ) + { + if ( level.selectedMove_forward < 0 ) + level.selectedMove_forward = 0; + + level.selectedMove_forward = level.selectedMove_forward + rate; + } + else + if ( buttonDown( "DPAD_DOWN" ) ) + { + if ( level.selectedMove_forward > 0 ) + level.selectedMove_forward = 0; + level.selectedMove_forward = level.selectedMove_forward - rate; + } + else + level.selectedMove_forward = 0; + + if ( buttonDown( "DPAD_RIGHT" ) ) + { + if ( level.selectedMove_right < 0 ) + level.selectedMove_right = 0; + + level.selectedMove_right = level.selectedMove_right + rate; + } + else + if ( buttonDown( "DPAD_LEFT" ) ) + { + if ( level.selectedMove_right > 0 ) + level.selectedMove_right = 0; + level.selectedMove_right = level.selectedMove_right - rate; + } + else + level.selectedMove_right = 0; + + if ( buttonDown( "BUTTON_Y" ) ) + { + if ( level.selectedMove_up < 0 ) + level.selectedMove_up = 0; + + level.selectedMove_up = level.selectedMove_up + rate; + } + else + if ( buttonDown( "BUTTON_B" ) ) + { + if ( level.selectedMove_up > 0 ) + level.selectedMove_up = 0; + level.selectedMove_up = level.selectedMove_up - rate; + } + else + level.selectedMove_up = 0; + +// vector = (level.selectedMove_right, level.selectedMove_forward, level.selectedMove_up); + vector = ( 0, 0, 0 ); + vector += ( forward * level.selectedMove_forward ); + vector += ( right * level.selectedMove_right ); + vector += ( up * level.selectedMove_up ); + + return vector; +} + + + +set_anglemod_move_vector() +{ + if (!level._createfx.snap90deg) + rate = level._createfx.rate; + else + rate = 90; + + if ( buttonDown( "kp_uparrow", "DPAD_UP" ) ) + { + if ( level.selectedRotate_pitch < 0 ) + level.selectedRotate_pitch = 0; + + level.selectedRotate_pitch = level.selectedRotate_pitch + rate; + } + else + if ( buttonDown( "kp_downarrow", "DPAD_DOWN" ) ) + { + if ( level.selectedRotate_pitch > 0 ) + level.selectedRotate_pitch = 0; + level.selectedRotate_pitch = level.selectedRotate_pitch - rate; + } + else + level.selectedRotate_pitch = 0; + + if ( buttonDown( "DPAD_LEFT" ) ) + { + if ( level.selectedRotate_yaw < 0 ) + level.selectedRotate_yaw = 0; + + level.selectedRotate_yaw = level.selectedRotate_yaw + rate; + } + else + if ( buttonDown( "DPAD_RIGHT" ) ) + { + if ( level.selectedRotate_yaw > 0 ) + level.selectedRotate_yaw = 0; + level.selectedRotate_yaw = level.selectedRotate_yaw - rate; + } + else + level.selectedRotate_yaw = 0; + + if ( buttonDown( "BUTTON_Y" ) ) + { + if ( level.selectedRotate_roll < 0 ) + level.selectedRotate_roll = 0; + + level.selectedRotate_roll = level.selectedRotate_roll + rate; + } + else + if ( buttonDown( "BUTTON_B" ) ) + { + if ( level.selectedRotate_roll > 0 ) + level.selectedRotate_roll = 0; + level.selectedRotate_roll = level.selectedRotate_roll - rate; + } + else + level.selectedRotate_roll = 0; + +} + +update_selected_entities() +{ + has_reactive_ents = false; + foreach ( ent in level._createfx.selected_fx_ents ) + { + if ( ent.v[ "type" ] == "reactive_fx" ) + has_reactive_ents = true; + + ent [[ level.func_updatefx ]](); + } + + if ( has_reactive_ents ) + refresh_reactive_fx_ents(); +} + +hack_start( painter_spmp ) +{ + if ( !IsDefined( painter_spmp ) ) + painter_spmp = "painter_mp"; + precachemenu( painter_spmp ); + wait .05; // make sure this wait stays above the return, since there is some logic in the calling code that assumes hack_start takes a frame. + if( painter_spmp == "painter_mp" ) + return; + + level.player openpopupmenu( painter_spmp );// painter.menu execs some console commands( ufo mode ).. sneaky hacks. + level.player closepopupmenu( painter_spmp ); +} + +stop_fx_looper() +{ + if ( IsDefined( self.looper ) ) + self.looper delete(); + self stop_loopsound(); +} + +stop_loopsound() +{ + self notify( "stop_loop" ); +} + +func_get_level_fx() +{ + AssertEx( IsDefined( level._effect ), "No effect aliases defined!" ); + + if ( !IsDefined( level._effect_keys ) ) + { + keys = GetArrayKeys( level._effect ); + } + else + { + keys = GetArrayKeys( level._effect ); + if ( keys.size == level._effect_keys.size ) + { + return level._effect_keys; + } + } + + println( "alphabetizing fx" ); + keys = alphabetize( keys ); + level._effect_keys = keys; + return keys; +} + +restart_fx_looper() +{ + stop_fx_looper(); + + //!!! new entities from copy/paste wont have a looper + + self set_forward_and_up_vectors(); + switch ( self.v[ "type" ] ) + { + case "loopfx": + self create_looper(); + break; + case "oneshotfx": + self create_triggerfx(); + break; + case "soundfx": + self create_loopsound(); + break; + case "soundfx_interval": + self create_interval_sound(); + break; + case "soundfx_dynamic": + self create_dynamicambience(); + break; + } +} + +refresh_reactive_fx_ents() +{ + level._fx.reactive_fx_ents = undefined; + foreach ( ent in level.createFXent ) + { + if ( ent.v[ "type" ] == "reactive_fx" ) + { + ent set_forward_and_up_vectors(); + ent add_reactive_fx(); + } + } +} + +process_fx_rotater() +{ + if ( level.fx_rotating ) + { + thread save_undo_buffer(); + level.createfx_last_movement_timer = 0; + return; + } + + set_anglemod_move_vector(); + + if ( !rotation_is_occuring() ) + { + return; + } + + level.fx_rotating = true; + + if ( level._createfx.selected_fx_ents.size > 1 && !level._createfx.localrot ) + { + center = get_center_of_array( level._createfx.selected_fx_ents ); + org = spawn( "script_origin", center ); + org.v[ "angles" ] = level._createfx.selected_fx_ents[ 0 ].v[ "angles" ]; + org.v[ "origin" ] = center; + + rotater = []; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + rotater[ i ] = spawn( "script_origin", level._createfx.selected_fx_ents[ i ].v[ "origin" ] ); + rotater[ i ].angles = level._createfx.selected_fx_ents[ i ].v[ "angles" ]; + rotater[ i ] LinkToSynchronizedParent( org ); + } + + // println ("pitch " + level.selectedRotate_pitch + " yaw " + level.selectedRotate_yaw); + + rotate_over_time( org, rotater ); + + org delete(); + + for ( i = 0; i < rotater.size; i++ ) + rotater[ i ] delete(); + } + else if ( level._createfx.selected_fx_ents.size >0 ) + { + foreach(ent in level._createfx.selected_fx_ents) + { + rotater = spawn( "script_origin", ( 0, 0, 0 ) ); + rotater.angles = ent.v[ "angles" ]; + if ( level.selectedRotate_pitch != 0 ) + rotater AddPitch( level.selectedRotate_pitch ); + else + if ( level.selectedRotate_yaw != 0 ) + rotater AddYaw( level.selectedRotate_yaw ); + else + rotater AddRoll( level.selectedRotate_roll ); + ent.v[ "angles" ] = rotater.angles; + rotater delete(); + } + wait( 0.05 ); + } + + level.fx_rotating = false; +} + +spawn_grenade() +{ +// if ( isSP() ) +// noself_func( "magicgrenade", level.createfxCursor[ "position" ] + ( 0, 0, 10 ), level.createfxCursor[ "position" ] ); +// else +// noself_func( "magicbullet", "frag_createfx_mp", level.createfxCursor[ "position" ] + ( 0, 0, 10 ), level.createfxCursor[ "position" ] ); + + // Fake nade + PlayFX( level._createfx.grenade.fx, level.createfxCursor[ "position" ] ); + level._createfx.grenade PlaySound( level._createfx.grenade.sound ); + RadiusDamage( level.createfxCursor[ "position" ], level._createfx.grenade.radius, 50, 5, undefined, "MOD_EXPLOSIVE" ); + level notify( "code_damageradius", undefined, level._createfx.grenade.radius, level.createfxCursor[ "position" ] ); +} + +//--------------------------------------------------------- +// Help Section +//--------------------------------------------------------- +show_help() +{ + //if help is already displayed, hide it by reselecting entitites + if(level.createfx_help_active == true) + { + clear_fx_hudElements(); + //need to reset menu here + //reselect_entitites(); + //clear_entity_selection(); + level.createfx_help_active = false; + level.createfx_menu_list_active = false; + reselect_entitites(); + //level.fx_highLightedEnt entity_highlight_enable(); + + } + else + { + level.createfx_help_active = true; + level.createfx_menu_list_active = true; + draw_help_list(); + thread help_navigation_buttons(); + clear_tool_hud(); + } + wait 0.2; +} + +//--------------------------------------------------------- +// Write CreateFX Section +//--------------------------------------------------------- +generate_fx_log( autosave ) +{ + // first lets fix all the really small numbers so they dont cause errors because the game will print out + // 4.2343-7e or whatever but cant accept it back in from script + + /# + check_createfx_limit(); + + flag_waitopen( "createfx_saving" ); + flag_set( "createfx_saving" ); + autosave = IsDefined( autosave ); + tab = "\t"; + + radiant_exploder_add_string = ""; + if( GetDvarInt( "scr_map_exploder_dump" ) ) + { + radiant_exploder_add_string = "_radiant_exploders"; + } + + createfx_filter_types(); + createfx_adjust_array(); + + // filename is deprecated +// filename = "createfx/" + get_template_level() + radiant_exploder_add_string + "_fx.gsc"; + +// if ( autosave ) +// { +// filename = "createfx/backup.gsc"; +// } + +// file = openfile( filename, "write" ); +// assertex( file != -1, "File not writeable (maybe you should check it out): " + filename ); + file = -1; + + +// func_cap = 700;// 700 is approximately 3500 lines in a function +// total_functions = level.createFXEnt.size / func_cap; +// for( i = 0; i < total_functions; i++ ) +// { +// cfxprintln( tab + "fx_" + ( i + 1 ) + "();" ); +// } + + // creates default values. This is to stop createfx files from being filled with redundant settings. + level._createfx.defaults = []; + level._createfx.defaults[ "exploder/delay" ] = getExploderDelayDefault(); + level._createfx.defaults[ "oneshotfx/delay" ] = getOneshotEffectDelayDefault(); + level._createfx.defaults[ "loopfx/delay" ] = getLoopEffectDelayDefault(); + level._createfx.defaults[ "soundfx_interval/delay_min" ] = getIntervalSoundDelayMinDefault(); + level._createfx.defaults[ "soundfx_interval/delay_max" ] = getIntervalSoundDelayMaxDefault(); + + + if ( isSP() ) + { + type = "fx"; + array = get_createfx_array( type ); + write_log( array, type, autosave, radiant_exploder_add_string ); + + type = "sound"; + array = get_createfx_array( type ); + write_log( array, type, autosave, radiant_exploder_add_string, undefined ); + } + else // MP, For now + { + if ( isdefined(level.tracked_ent) ) + { + if ( !isdefined(level.tracked_ents) ) + level.tracked_ents = []; + level.tracked_ents[level.tracked_ents.size] = level.tracked_ent.v; + level.tracked_ent = undefined; + } + write_log( level.createFXEnt, "fx", autosave, radiant_exploder_add_string, level.tracked_ents ); + } + +// saved = closefile( file ); +// assertex( saved == 1, "File not saved (see above message?): " + filename ); + flag_clear( "createfx_saving" ); + +// println( "CreateFX entities placed: " + level.createFxEnt.size ); + #/ +} + +write_entity( e, autosave ) +{ + tab = "\t"; + assertEX( IsDefined( e.v[ "type" ] ), "effect at origin " + e.v[ "origin" ] + " has no type" ); + + // don't post .map effects in the script. +// if (e.v["worldfx"]) +// continue; + + // when scr_map_exploder_dump is set just output the exploders from radiant. could output two scripts but keeping it simple. + if( GetDvarInt("scr_map_exploder_dump") ) + { + if ( !IsDefined( e.model ) ) + return; + } + else if ( IsDefined( e.model ) ) + { + return; // entities with models are from radiant and don't get reported + } + + if ( e.v[ "type" ] == "loopfx" ) + { + cfxprintln( tab + "ent = createLoopEffect( \"" + e.v[ "fxid" ] + "\" );" ); + } + + if ( e.v[ "type" ] == "oneshotfx" ) + { + cfxprintln( tab + "ent = createOneshotEffect( \"" + e.v[ "fxid" ] + "\" );" ); + } + + if ( e.v[ "type" ] == "exploder" ) + { + if( IsDefined( e.v[ "exploder" ] ) && !level.mp_createfx ) + { + cfxprintln( tab + "ent = createExploderEx( \"" + e.v[ "fxid" ] + "\", \"" + e.v[ "exploder" ] + "\" );" ); + } + else + { + cfxprintln( tab + "ent = createExploder( \"" + e.v[ "fxid" ] + "\" );" ); + } + } + + if ( e.v[ "type" ] == "soundfx" ) + { + cfxprintln( tab + "ent = createLoopSound();" ); + } + + if ( e.v[ "type" ] == "soundfx_interval" ) + { + cfxprintln( tab + "ent = createIntervalSound();" ); + } + + if ( e.v[ "type" ] == "reactive_fx" ) + { + cfxprintln( tab + "ent = createReactiveEnt();" ); + } + + if ( e.v[ "type" ] == "soundfx_dynamic" ) + { + cfxprintln( tab + "ent = createDynamicAmbience();" ); + } + + cfxprintln( tab + "ent set_origin_and_angles( " + e.v[ "origin" ] + ", " + e.v[ "angles" ] + " );" ); + + print_fx_options( e, tab, autosave ); + cfxprintln( "" ); +} + +write_log( array, type, autosave, radiant_exploder_add_string, secondArray ) +{ + tab = "\t"; + cfxprintlnStart(); + cfxprintln( "//_createfx generated. Do not touch!!" ); + cfxprintln( "#include common_scripts\\utility;" ); + cfxprintln( "#include common_scripts\\_createfx;\n" ); + cfxprintln( "" ); + + cfxprintln( "main()" ); + cfxprintln( "{" ); + numPlaced = array.size; + if ( isDefined( secondArray ) ) + numPlaced = numPlaced + secondArray.size; + cfxprintln( tab + "// CreateFX " + type + " entities placed: " + numPlaced ); + + foreach ( e in array ) + { + if ( level.createfx_loopcounter > 16 ) + { + level.createfx_loopcounter = 0; + wait .1; // give IWLauncher a chance to keep up + } + level.createfx_loopcounter++; + write_entity( e, autosave ); + } + + if ( isdefined( secondArray ) ) + { + foreach ( a in secondArray ) + { + if ( level.createfx_loopcounter > 16 ) + { + level.createfx_loopcounter = 0; + wait .1; // give IWLauncher a chance to keep up + } + level.createfx_loopcounter++; + e = SpawnStruct(); + e.v = a; + write_entity( e, autosave ); + } + } + cfxprintln( "}" ); + cfxprintln( " " ); + cfxprintlnEnd( autosave, radiant_exploder_add_string, type ); +} + +createfx_adjust_array() +{ + limit = 0.1; + foreach ( ent in level.createFXent ) + { + origin = []; + angles = []; + for ( i = 0;i < 3;i++ ) + { + origin[ i ] = ent.v[ "origin" ][ i ]; + angles[ i ] = ent.v[ "angles" ][ i ]; + + if ( origin[ i ] < limit && origin[ i ] > limit * - 1 ) + { + origin[ i ] = 0; + } + + if ( angles[ i ] < limit && angles[ i ] > limit * - 1 ) + { + angles[ i ] = 0; + } + } + + ent.v[ "origin" ] = ( origin[ 0 ], origin[ 1 ], origin[ 2 ] ); + ent.v[ "angles" ] = ( angles[ 0 ], angles[ 1 ], angles[ 2 ] ); + } +} + +get_createfx_array( type ) +{ + types = get_createfx_types( type ); + + array = []; + foreach ( index, _ in types ) + { + array[ index ] = []; + } + + foreach ( ent in level.createFXent ) + { + found_type = false; + foreach ( index, type in types ) + { + if ( ent.v[ "type" ] != type ) + continue; + + found_type = true; + array[ index ][ array[ index ].size ] = ent; + break; + } + } + + new_array = []; + for ( i = 0; i < types.size; i++ ) + { + foreach ( ent in array[ i ] ) + { + new_array[ new_array.size ] = ent; + } + } + + return new_array; +} + +get_createfx_types( type ) +{ + types = []; + if ( type == "fx" ) + { + types[ 0 ] = "loopfx"; + types[ 1 ] = "oneshotfx"; + types[ 2 ] = "exploder"; + } + else // sound + { + types[ 0 ] = "soundfx"; + types[ 1 ] = "soundfx_interval"; + types[ 2 ] = "reactive_fx"; + types[ 3 ] = "soundfx_dynamic"; + } + + return types; +} + +is_createfx_type( ent, type ) +{ + types = get_createfx_types( type ); + + foreach ( t in types ) + { + if ( ent.v[ "type" ] == t ) + { + return true; + } + } + + return false; +} + +createfx_filter_types() +{ + types = []; + types[ 0 ] = "soundfx"; + types[ 1 ] = "loopfx"; + types[ 2 ] = "oneshotfx"; + types[ 3 ] = "exploder"; + types[ 4 ] = "soundfx_interval"; + types[ 5 ] = "reactive_fx"; + types[ 6 ] = "soundfx_dynamic"; + + array = []; + foreach ( index, _ in types ) + { + array[ index ] = []; + } + + foreach ( ent in level.createFXent ) + { + found_type = false; + foreach ( index, type in types ) + { + if ( ent.v[ "type" ] != type ) + continue; + + found_type = true; + array[ index ][ array[ index ].size ] = ent; + break; + } + + assertex( found_type, "Didnt understand createfx type " + ent.v[ "type" ] ); + } + + new_array = []; + for ( i = 0; i < types.size; i++ ) + { + foreach ( ent in array[ i ] ) + { + new_array[ new_array.size ] = ent; + } + } + + level.createFXent = new_array; +} + +cfxprintlnStart() +{ + fileprint_launcher_start_file(); +} + +cfxprintln( string ) +{ + fileprint_launcher( string ); +} + +cfxprintlnEnd( autosave, radiant_exploder_add_string, type ) +{ + bP4add = true; + + if( radiant_exploder_add_string != "" || autosave ) + { + bP4add = false; + } + + if ( isSP() ) + { + scriptname = get_template_level() + radiant_exploder_add_string + "_" + type + ".gsc"; + if ( autosave ) + { + scriptname = "backup" + "_" + type + ".gsc"; + } + } + else // MP, for now + { + scriptname = get_template_level() + radiant_exploder_add_string + "_" + type + ".gsc"; + if ( autosave ) + { + scriptname = "backup.gsc"; + } + } + + fileprint_launcher_end_file( "/share/raw/maps/createfx/" + scriptname, bP4add ); +} + +//--------------------------------------------------------- +// Button Section +//--------------------------------------------------------- +process_button_held_and_clicked() +{ + add_button( "mouse1" ); + add_button( "BUTTON_RSHLDR" ); + add_button( "BUTTON_LSHLDR" ); + add_button( "BUTTON_RSTICK" ); + add_button( "BUTTON_LSTICK" ); + add_button( "BUTTON_A" ); + add_button( "BUTTON_B" ); + add_button( "BUTTON_X" ); + add_button( "BUTTON_Y" ); + add_button( "DPAD_UP" ); + add_button( "DPAD_LEFT" ); + add_button( "DPAD_RIGHT" ); + add_button( "DPAD_DOWN" ); + + + add_kb_button( "shift" ); + add_kb_button( "ctrl" ); + add_kb_button( "escape" ); + add_kb_button( "F1" ); + add_kb_button( "F5" ); + add_kb_button( "F4" ); + add_kb_button( "F2" ); + add_kb_button( "a" ); + add_kb_button( "g" ); + add_kb_button( "c" ); + add_kb_button( "h" ); + add_kb_button( "i" ); + add_kb_button( "f" ); + add_kb_button( "k" ); + add_kb_button( "l" ); + add_kb_button( "m" ); + add_kb_button( "o" ); + add_kb_button( "p" ); + add_kb_button( "r" ); + add_kb_button( "s" ); + add_kb_button( "u" ); + add_kb_button( "v" ); + add_kb_button( "x" ); + add_kb_button( "y" ); + add_kb_button( "z" ); + add_kb_button( "del" );// DEL is allowed to be pressed while in select mode + add_kb_button( "end" ); + add_kb_button( "tab" ); + add_kb_button( "ins" ); + add_kb_button( "add" ); + add_kb_button( "space" ); + add_kb_button( "enter" ); + add_kb_button( "1" ); + add_kb_button( "2" ); + add_kb_button( "3" ); + add_kb_button( "4" ); + add_kb_button( "5" ); + add_kb_button( "6" ); + add_kb_button( "7" ); + add_kb_button( "8" ); + add_kb_button( "9" ); + add_kb_button( "0" ); + add_kb_button( "-" ); + add_kb_button( "=" ); + add_kb_button( "," ); + add_kb_button( "." ); + add_kb_button( "[" ); + add_kb_button( "]" ); + add_kb_button( "leftarrow" ); + add_kb_button( "rightarrow" ); + add_kb_button( "uparrow" ); + add_kb_button( "downarrow" ); +} + + +locked( name ) +{ + if ( IsDefined( level._createfx.lockedList[ name ] ) ) + return false; + + return kb_locked( name ); +} + +kb_locked( name ) +{ + return level.createfx_inputlocked && IsDefined( level.button_is_kb[ name ] ); +} + + +add_button( name ) +{ + if ( locked( name ) ) + return; + + if ( !IsDefined( level.buttonIsHeld[ name ] ) ) + { + if ( level.player buttonPressed( name ) ) + { + level.buttonIsHeld[ name ] = true; + level.buttonClick[ name ] = true; +// println("Button: " + name); + } + } + else + { + if ( !level.player buttonPressed( name ) ) + { + level.buttonIsHeld[ name ] = undefined; + } + } +} + +add_kb_button( name ) +{ + level.button_is_kb[ name ] = true; + add_button( name ); +} + +buttonDown( button, button2 ) +{ + return buttonPressed_internal( button ) || buttonPressed_internal( button2 ); +} + +buttonPressed_internal( button ) +{ + if ( !IsDefined( button ) ) + return false; + + // keyboard buttons can be locked so you can type in the fx info on the keyboard without + // accidentally activating features + if ( kb_locked( button ) ) + return false; + + return level.player buttonPressed( button ); +} + +button_is_held( name, name2 ) +{ + if ( IsDefined( name2 ) ) + { + if ( IsDefined( level.buttonIsHeld[ name2 ] ) ) + return true; + } + return IsDefined( level.buttonIsHeld[ name ] ); +} + +button_is_clicked( name, name2 ) +{ + if ( IsDefined( name2 ) ) + { + if ( IsDefined( level.buttonClick[ name2 ] ) ) + return true; + } + return IsDefined( level.buttonClick[ name ] ); +} + +//--------------------------------------------------------- +// HUD Section +//--------------------------------------------------------- + +init_huds() +{ + level._createfx.hudelems = []; + level._createfx.hudElem_count = 30; + // all this offset stuff lets us duplicate the text which puts an outline around + // it and makes it more legible + strOffsetX = []; + strOffsetY = []; + strOffsetX[ 0 ] = 0; + strOffsetY[ 0 ] = 0; + strOffsetX[ 1 ] = 1; + strOffsetY[ 1 ] = 1; + strOffsetX[ 2 ] = -2; + strOffsetY[ 2 ] = 1; + strOffsetX[ 3 ] = 1; + strOffsetY[ 3 ] = -1; + strOffsetX[ 4 ] = -2; + strOffsetY[ 4 ] = -1; + + // setup the free text marker to allow some permanent strings + level.clearTextMarker = newHudElem(); + level.clearTextMarker.alpha = 0; + level.clearTextMarker setDevText( "marker" ); + + for ( i = 0;i < level._createfx.hudelem_count;i++ ) + { + newStrArray = []; + for ( p = 0;p < 1;p++ ) + { + newStr = newHudElem(); + newStr.alignX = "left"; + newStr.location = 0; + newStr.foreground = 1; + newStr.fontScale = 1.40; + newStr.sort = 20 - p; + newStr.alpha = 1; + newStr.x = 0 + strOffsetX[ p ]; + newStr.y = 60 + strOffsetY[ p ] + i * 15; + + if ( p > 0 ) + { + newStr.color = ( 0, 0, 0 ); + } + + newStrArray[ newStrArray.size ] = newStr; + } + + level._createfx.hudelems[ i ] = newStrArray; + } + + newStrArray = []; + for ( p = 0; p < 5; p++ ) + { + // setup instructional text + newStr = newHudElem(); + newStr.alignX = "center"; + newStr.location = 0; + newStr.foreground = 1; + newStr.fontScale = 1.4; + newStr.sort = 20 - p; + newStr.alpha = 1; + newStr.x = 320 + strOffsetX[ p ]; + newStr.y = 80 + strOffsetY[ p ]; + if ( p > 0 ) + { + newStr.color = ( 0, 0, 0 ); + } + + newStrArray[ newStrArray.size ] = newStr; + } + + level.createFX_centerPrint = newStrArray; +} + +init_crosshair() +{ + // setup "crosshair" + crossHair = newHudElem(); + crossHair.location = 0; + crossHair.alignX = "center"; + crossHair.alignY = "middle"; + crossHair.foreground = 1; + crossHair.fontScale = 2; + crossHair.sort = 20; + crossHair.alpha = 1; + crossHair.x = 320; + crossHair.y = 233; + crossHair setDevText( "." ); +} + +clear_fx_hudElements() +{ + level.clearTextMarker ClearAllTextAfterHudElem(); + + for ( i = 0;i < level._createfx.hudelem_count;i++ ) + { + for ( p = 0; p < 1; p++ ) + level._createfx.hudelems[ i ][ p ] setDevText( "" ); + } + + level.fxHudElements = 0; +} + +set_fx_hudElement( text ) +{ + for ( p = 0;p < 1;p++ ) + level._createfx.hudelems[ level.fxHudElements ][ p ] setDevText( text ); + + level.fxHudElements++; + assert( level.fxHudElements < level._createfx.hudelem_count ); +} + +init_tool_hud() +{ + if ( !IsDefined( level._createfx.tool_hudelems ) ) + { + level._createfx.tool_hudelems = []; + } + + if ( !IsDefined( level._createfx.tool_hud_visible ) ) + { + level._createfx.tool_hud_visible = true; + } + + if ( !IsDefined( level._createfx.tool_hud ) ) + { + level._createfx.tool_hud = ""; + } +} + +new_tool_hud( name ) +{ + foreach ( idx, hud in level._createfx.tool_hudelems ) + { + if ( IsDefined( hud.value_hudelem ) ) + { + hud.value_hudelem Destroy(); + } + + hud Destroy(); + + level._createfx.tool_hudelems[ idx ] = undefined; + } + + level._createfx.tool_hud = name; +} + +current_mode_hud( name ) +{ + return level._createfx.tool_hud == name; +} + +clear_tool_hud() +{ + new_tool_hud( "" ); +} + +new_tool_hudelem( n ) +{ + hud = newHudElem(); + hud.alignX = "left"; + hud.location = 0; + hud.foreground = 1; + hud.fontScale = 1.2; + hud.alpha = 1; + hud.x = 0; + hud.y = 320 + ( n * 15 ); + return hud; +} + +get_tool_hudelem( name ) +{ + if ( IsDefined( level._createfx.tool_hudelems[ name ] ) ) + { + return level._createfx.tool_hudelems[ name ]; + } + + return undefined; +} + +set_tool_hudelem( var, value, color ) +{ + hud = get_tool_hudelem( var ); + + if ( !IsDefined( hud ) ) + { + hud = new_tool_hudelem( level._createfx.tool_hudelems.size ); + level._createfx.tool_hudelems[ var ] = hud; + hud SetDevText( var ); + hud.text = var; + } + + if ( IsDefined( value ) ) + { + if ( IsDefined( hud.value_hudelem ) ) + { + value_hud = hud.value_hudelem; + } + else + { + value_hud = new_tool_hudelem( level._createfx.tool_hudelems.size ); + value_hud.x += 110; + value_hud.y = hud.y; + hud.value_hudelem = value_hud; + } + + if ( IsDefined( value_hud.text ) && value_hud.text == value ) + { + return; + } + + value_hud SetDevText( value ); + value_hud.text = value; + if (!isDefined(color)) + color = (1,1,1); + value_hud.color = color; + } +} + +select_by_substring() +{ + substring = GetDvar( "select_by_substring" ); + if ( substring == "" ) + { + return false; + } + + SetDvar( "select_by_substring", "" ); + + index_array = []; + foreach ( i, ent in level.createFXent ) + { + if ( IsSubStr( ent.v[ "fxid" ], substring ) ) + { + index_array[ index_array.size ] = i; + } + } + + if ( index_array.size == 0 ) + { + PrintLn( "^1select_by_substring could not find \"" + substring + "\"" ); + return false; + } + + deselect_all_ents(); + select_index_array( index_array ); + + foreach ( index in index_array ) + { + ent = level.createFXent[ index ]; + select_entity( index, ent ); + } + + PrintLn( "select_by_substring found \"" + substring + "\" [" + index_array.size + "]" ); + return true; +} + +select_index_array( index_array ) +{ + foreach ( index in index_array ) + { + ent = level.createFXent[ index ]; + select_entity( index, ent ); + } +} + +deselect_all_ents() +{ + foreach ( i, ent in level._createfx.selected_fx_ents ) + { + deselect_entity( i, ent ); + } +} +setup_last_movement_timer() +{ + wait 0.5; + for ( ;; ) + { + level.createfx_last_movement_timer += 0.05; + if (level.createfx_last_movement_timer == 0.15) + { + foreach (ent in level._createfx.selected_fx_ents) + { + if (ent.v[ "type" ] == "exploder") + ent activate_individual_exploder(); + } + display_current_translation(); + save_redo_buffer(); + } + //update translation info in hud + if (level.createfx_last_movement_timer == 0.05) + { + ent = get_last_selected_ent(); + display_current_translation(); + } + wait 0.05; + } +} + +frame_selected() +{ + if(level._createfx.selected_fx_ents.size < 1) + return; + + if ( level._createfx.selected_fx_ents.size > 1 ) + { + center = get_center_of_array( level._createfx.selected_fx_ents ); + camradius = get_radius_of_array( level._createfx.selected_fx_ents ) + 200; + } + else + { + center = level._createfx.selected_fx_ents[ 0 ].v[ "origin" ]; + camradius = 200; + } + forward = anglestoforward( level.player getplayerangles() ); + camOffset = forward * (-1 * camradius); + eyePos = level.player getEye(); + eyeOffset = eyePos - level.player.origin; + level.player SetOrigin( center + camOffset - eyeOffset); + +} + +clear_all_loopers() +{ + foreach(ent in level.createFXent) + { + if ( IsDefined( ent.looper ) ) + ent.looper delete(); + ent stop_loopsound(); + } +} + +restart_oneshots() +{ + foreach(ent in level.createFXent) + { + if ( ent.v[ "type" ] == "oneshotfx" ) + { + ent restart_fx_looper(); + } + } +} + +restart_selected_exploders() +{ + foreach (ent in level._createfx.selected_fx_ents) + { + if (isDefined(ent) && ent.v[ "type" ] == "exploder") + ent activate_individual_exploder(); + } +} + +save_undo_buffer() +{ + if ( isDefined( level.createFxent ) && level.createfx_last_movement_timer > 0.15 ) + level.createfxent_undo = copyStructArrayValues(level.createFXent); +} + +save_redo_buffer() +{ + if ( isDefined( level.createFxent) ) + level.createfxent_redo = copyStructArrayValues(level.createFXent); +} + +undo() +{ + if ( isDefined( level.createfxent_undo ) ) + { + clear_all_loopers(); + level.createFXent = []; + level.createFXent = copyStructArrayValues(level.createFXent_undo); + clear_fx_hudElements(); + reselect_entitites(); + restart_oneshots(); + restart_selected_exploders(); + } +} + +redo() +{ + if(isDefined(level.createfxent_redo)) + { + clear_all_loopers(); + level.createFXent = []; + level.createFXent = copyStructArrayValues(level.createFXent_redo); + clear_fx_hudElements(); + reselect_entitites(); + restart_oneshots(); + restart_selected_exploders(); + } +} + +copyStructArrayValues(structArray) +{ + //outputArray = structArray; + outputArray = []; + if(structArray.size > 0) + { + for( a = 0; a < structArray.size; a++ ) + { + ent = spawnStruct(); + if( isDefined(structArray[ a ].v) ) + { + //copy struct values and then set new array to use them + ent.v = []; + ent.v[ "type" ] = structArray[a].v[ "type" ]; + ent.v[ "fxid" ] = structArray[a].v[ "fxid" ]; + ent.v[ "soundalias" ] = structArray[a].v[ "soundalias" ]; + ent.v[ "loopsound" ] = structArray[a].v[ "loopsound" ]; + ent.v[ "angles" ] = structArray[a].v[ "angles" ]; + ent.v[ "origin" ] = structArray[a].v[ "origin" ]; + ent.v[ "exploder" ] = structArray[a].v[ "exploder" ]; + ent.v[ "flag" ] = structArray[a].v[ "flag" ]; + ent.v[ "exploder_type" ] = structArray[a].v[ "exploder_type" ]; + ent.v[ "server_culled" ] = structArray[a].v[ "server_culled" ]; + ent.v[ "delay_min" ] = structArray[a].v[ "delay_min" ]; + ent.v[ "delay_max" ] = structArray[a].v[ "delay_max" ]; + ent.v[ "soundalias" ] = structArray[a].v[ "soundalias" ]; + ent.v[ "delay" ] = structArray[a].v[ "delay" ]; + ent.v[ "forward" ] = structArray[a].v[ "forward" ]; + ent.v[ "up" ] = structArray[a].v[ "up" ]; + + outputArray[ a ] = ent; + } + outputArray[ a ].drawn = structArray[ a ].drawn; + outputArray[ a ].textalpha = structArray[ a ].textalpha; + } + } + return outputArray; +} + + +removeFXentWithEntity( entity ) +{ + new_array = []; + // remove the ent with this model(which is an entity:) + // other routines expect a contiguous createFXent array, so rebuild the whole thing + foreach ( idx, ent in level.createFXent ) + { + if ( isDefined( ent.model ) && ( ent.model == entity ) ) + { + } + else + new_array[new_array.size] = ent; + } + level.createFXent = new_array; +} \ No newline at end of file diff --git a/raw/common_scripts/_createfxmenu.gsc b/raw/common_scripts/_createfxmenu.gsc new file mode 100644 index 0000000..173464b --- /dev/null +++ b/raw/common_scripts/_createfxmenu.gsc @@ -0,0 +1,968 @@ +#include common_scripts\utility; +#include common_scripts\_createfx; + + +//--------------------------------------------------------- +// Menu init/loop section +//--------------------------------------------------------- +init_menu() +{ + level._createfx.options = []; + // each option has a type, a name its stored under, a description, a default, and a mask it uses to determine + // which types of fx can have this option + addOption( "vector", "origin", "Origin", (0,0,0), "fx", 1 ); + addOption( "vector", "angles", "Angles", (0,0,0), "fx", 1 ); + addOption( "string", "fxid", "FX id", "nil", "fx" ); + addOption( "float", "delay", "Repeat rate/start delay", 0.5, "fx" ); + addOption( "string", "flag", "Flag", "nil", "exploder" ); + addOption( "string", "platform", "Platform", "all", "all" ); + + if ( !level.mp_createfx ) + { + addOption( "string", "firefx", "2nd FX id", "nil", "exploder" ); + addOption( "float", "firefxdelay", "2nd FX id repeat rate", 0.5, "exploder" ); + addOption( "float", "firefxtimeout", "2nd FX timeout", 5, "exploder" ); + addOption( "string", "firefxsound", "2nd FX soundalias", "nil", "exploder" ); + addOption( "float", "damage", "Radius damage", 150, "exploder" ); + addOption( "float", "damage_radius", "Radius of radius damage", 250, "exploder" ); + addOption( "string", "earthquake", "Earthquake", "nil", "exploder" ); + addOption( "string", "ender", "Level notify for ending 2nd FX", "nil", "exploder" ); + } + + addOption( "float", "delay_min", "Minimimum time between repeats", 1, "soundfx_interval" ); + addOption( "float", "delay_max", "Maximum time between repeats", 2, "soundfx_interval" ); + addOption( "int", "repeat", "Number of times to repeat", 5, "exploder" ); + addOption( "string", "exploder", "Exploder", "1", "exploder" ); + setup_help_keys(); + + + addOption( "string", "soundalias", "Soundalias", "nil", "all" ); + addOption( "string", "loopsound", "Loopsound", "nil", "exploder" ); + + addOption( "int", "reactive_radius", "Reactive Radius", 100, "reactive_fx", undefined, ::input_reactive_radius ); + + addOption( "string", "ambiencename", "Ambience Name", "nil", "soundfx_dynamic" ); + addOption( "int", "dynamic_distance", "Dynamic Max Distance", 1000, "soundfx_dynamic" ); + + if( !level.mp_createfx ) + { + addOption( "string", "rumble", "Rumble", "nil", "exploder" ); + addOption( "int", "stoppable", "Can be stopped from script", "1", "all" ); + } + + level.effect_list_offset = 0; + level.effect_list_offset_max = 10; + level.effect_list_current_size = 0; + + level.help_list_offset = 0; + level.help_list_offset_max = 20; + + level.createfx_help_active = false; + + level.createfx_menu_list_active = false; + + // creates mask groups. For example if the above says its mask is "fx", then all the types under "fx" can use the option + level.createfxMasks = []; + level.createfxMasks[ "all" ] = []; + level.createfxMasks[ "all" ][ "exploder" ] = true; + level.createfxMasks[ "all" ][ "oneshotfx" ] = true; + level.createfxMasks[ "all" ][ "loopfx" ] = true; + level.createfxMasks[ "all" ][ "soundfx" ] = true; + level.createfxMasks[ "all" ][ "soundfx_interval" ] = true; + level.createfxMasks[ "all" ][ "reactive_fx" ] = true; + level.createfxMasks[ "all" ][ "soundfx_dynamic" ] = true; + + level.createfxMasks[ "fx" ] = []; + level.createfxMasks[ "fx" ][ "exploder" ] = true; + level.createfxMasks[ "fx" ][ "oneshotfx" ] = true; + level.createfxMasks[ "fx" ][ "loopfx" ] = true; + + level.createfxMasks[ "exploder" ] = []; + level.createfxMasks[ "exploder" ][ "exploder" ] = true; + + level.createfxMasks[ "loopfx" ] = []; + level.createfxMasks[ "loopfx" ][ "loopfx" ] = true; + + level.createfxMasks[ "oneshotfx" ] = []; + level.createfxMasks[ "oneshotfx" ][ "oneshotfx" ] = true; + + level.createfxMasks[ "soundfx" ] = []; + level.createfxMasks[ "soundfx" ][ "soundalias" ] = true; + + level.createfxMasks[ "soundfx_interval" ] = []; + level.createfxMasks[ "soundfx_interval" ][ "soundfx_interval" ] = true; + + level.createfxMasks[ "reactive_fx" ] = []; + level.createfxMasks[ "reactive_fx" ][ "reactive_fx" ] = true; + + level.createfxMasks[ "soundfx_dynamic" ] = []; + level.createfxMasks[ "soundfx_dynamic" ][ "soundfx_dynamic" ] = true; + + // Mainly used for input of a menu + menus = []; + menus[ "creation" ] = ::menu_create_select; + menus[ "create_oneshot" ] = ::menu_create; + menus[ "create_loopfx" ] = ::menu_create; + menus[ "change_fxid" ] = ::menu_create; + menus[ "none" ] = ::menu_none; + menus[ "add_options" ] = ::menu_add_options; + menus[ "select_by_name" ] = ::menu_select_by_name; + + level._createfx.menus = menus; +} + +menu( name ) +{ + return level.create_fx_menu == name; +} + +setmenu( name ) +{ + level.create_fx_menu = name; +} + +create_fx_menu() +{ + if ( button_is_clicked( "escape", "x" ) ) + { + _exit_menu(); + return; + } + + if ( IsDefined( level._createfx.menus[ level.create_fx_menu ] ) ) + { + [[ level._createfx.menus[ level.create_fx_menu ] ]](); + } +} + +menu_create_select() +{ + if ( button_is_clicked( "1" ) ) + { + setmenu( "create_oneshot" ); + draw_effects_list(); + return; + } + else if ( button_is_clicked( "2" ) ) + { + setmenu( "create_loopfx" ); + draw_effects_list(); + return; + } + else if ( button_is_clicked( "3" ) ) + { + setmenu( "create_loopsound" ); + ent = createLoopSound(); + finish_creating_entity( ent ); + return; + } + else if ( button_is_clicked( "4" ) ) + { + setmenu( "create_exploder" ); + ent = createNewExploder(); + finish_creating_entity( ent ); + return; + } + else if ( button_is_clicked( "5" ) ) + { + setmenu( "create_interval_sound" ); + ent = createIntervalSound(); + finish_creating_entity( ent ); + return; + } + else if ( button_is_clicked( "6" ) ) + { + ent = createReactiveEnt(); + finish_creating_entity( ent ); + return; +} + else if ( button_is_clicked( "7" ) ) + { + ent = createDynamicAmbience(); + finish_creating_entity( ent ); + return; + } +} + +menu_create() +{ + level.createfx_menu_list_active = true; + if ( next_button() ) + { + increment_list_offset(); + draw_effects_list(); + } + else if ( previous_button() ) + { + decrement_list_offset(); + draw_effects_list(); + } + + menu_fx_creation(); +} + +menu_none() +{ + if ( button_is_clicked( "m" ) ) + increment_list_offset(); + + // change selected entities + menu_change_selected_fx(); + + // if there's a selected ent then display the info on the last one to be selected + if ( entities_are_selected() ) + { + last_selected_ent = get_last_selected_ent(); + + // only update hudelems when we have new info + if ( !IsDefined( level.last_displayed_ent ) || last_selected_ent != level.last_displayed_ent || level._createfx.justConvertedOneshot == 1 ) + { + display_fx_info( last_selected_ent ); + level.last_displayed_ent = last_selected_ent; + level._createfx.justConvertedOneshot = 0; + } + + if ( button_is_clicked( "a" ) ) + { + clear_settable_fx(); + setMenu( "add_options" ); + } + } + else + { + level.last_displayed_ent = undefined; +} +} + +menu_add_options() +{ + if ( !entities_are_selected() ) + { + clear_fx_hudElements(); + setMenu( "none" ); + return; + } + + display_fx_add_options( get_last_selected_ent() ); + if ( next_button() ) + { + increment_list_offset(); +// draw_effects_list(); + } +} + +menu_select_by_name() +{ + if ( next_button() ) + { + increment_list_offset(); + draw_effects_list( "Select by name" ); + } + else if ( previous_button() ) + { + decrement_list_offset(); + draw_effects_list( "Select by name" ); + } + + select_by_name(); +} + +next_button() +{ + return button_is_clicked( "rightarrow" ); +} + +previous_button() +{ + return button_is_clicked( "leftarrow" ); +} + +_exit_menu() +{ + clear_fx_hudElements(); + clear_entity_selection(); + update_selected_entities(); + setmenu( "none" ); +} + +//--------------------------------------------------------- +// Create FX Section (button presses) +//--------------------------------------------------------- +menu_fx_creation() +{ + count = 0; + picked_fx = undefined; + keys = func_get_level_fx(); + + for ( i = level.effect_list_offset; i < keys.size; i++ ) + { + count = count + 1; + button_to_check = count; + if ( button_to_check == 10 ) + button_to_check = 0; + if ( button_is_clicked( button_to_check + "" ) ) + { + picked_fx = keys[ i ]; + break; + } + + if ( count > level.effect_list_offset_max ) + break; + } + + if ( !isdefined( picked_fx ) ) + return; + + if ( menu( "change_fxid" ) ) + { + apply_option_to_selected_fx( get_option( "fxid" ), picked_fx ); + level.effect_list_offset = 0; + clear_fx_hudElements(); + setMenu( "none" ); + level.createfx_menu_list_active = false; + level.createfx_last_movement_timer = 0; + return; + } + + + ent = undefined; + if ( menu( "create_loopfx" ) ) + ent = createLoopEffect( picked_fx ); + if ( menu( "create_oneshot" ) ) + ent = createOneshotEffect( picked_fx ); + + finish_creating_entity( ent ); +} + +finish_creating_entity( ent ) +{ + assert( isdefined( ent ) ); + ent.v[ "angles" ] = vectortoangles( ( ent.v[ "origin" ] + ( 0, 0, 100 ) ) - ent.v[ "origin" ] ); + ent post_entity_creation_function();// for createfx dev purposes + clear_entity_selection(); + select_last_entity(); + move_selection_to_cursor(); + update_selected_entities(); + setMenu( "none" ); + level.createfx_menu_list_active = false; +} + +entities_are_selected() +{ + return level._createfx.selected_fx_ents.size > 0; +} + +menu_change_selected_fx() +{ + if ( !level._createfx.selected_fx_ents.size ) + { + return; + } + + count = 0; + drawnCount = 0; + ent = get_last_selected_ent(); + + for ( i = 0; i < level._createfx.options.size; i++ ) + { + option = level._createfx.options[ i ]; + if ( !isdefined( ent.v[ option[ "name" ] ] ) ) + continue; + count++ ; + if ( count < level.effect_list_offset ) + continue; + + drawnCount++ ; + button_to_check = drawnCount; + if ( button_to_check == 10 ) + button_to_check = 0; + + if ( button_is_clicked( button_to_check + "" ) ) + { + prepare_option_for_change( option, drawnCount ); + break; + } + + if ( drawnCount > level.effect_list_offset_max ) + { + more = true; + break; + } + } +} + +prepare_option_for_change( option, drawnCount ) +{ + if ( option[ "name" ] == "fxid" ) + { + setMenu( "change_fxid" ); + draw_effects_list(); + return; + } + + level.createfx_inputlocked = true; + level._createfx.hudelems[ drawnCount + 1 ][ 0 ].color = ( 1, 1, 0 ); + + if ( IsDefined( option[ "input_func" ] ) ) + { + thread [[ option[ "input_func" ] ]]( drawnCount + 1 ); + } + else + { + createfx_centerprint( "To set " + option[ "description" ] + ", type /fx newvalue. To remove "+ option[ "description" ] +", type /fx del" ); + } + + set_option_index( option[ "name" ] ); + setdvar( "fx", "nil" ); +} + +menu_fx_option_set() +{ + if ( getdvar( "fx" ) == "nil" ) + return; + + if ( getdvar( "fx" ) == "del" ) + { + remove_selected_option(); + return; + } + + option = get_selected_option(); + setting = undefined; + if ( option[ "type" ] == "string" ) + setting = getdvar( "fx" ); + if ( option[ "type" ] == "int" ) + setting = getdvarint( "fx" ); + if ( option[ "type" ] == "float" ) + setting = getdvarfloat( "fx" ); + if ( option[ "type" ] == "vector" ) + setting = getdvarvector( "fx" ); + + if(isDefined(setting)) + apply_option_to_selected_fx( option, setting ); + else + setdvar( "fx" , "nil"); +} + +apply_option_to_selected_fx( option, setting ) +{ + save_undo_buffer(); + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + + if ( mask( option[ "mask" ], ent.v[ "type" ] )) + ent.v[ option[ "name" ] ] = setting; + } + + level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent + update_selected_entities(); + clear_settable_fx(); + //if we moved it, restart and focus camera on new position + if( option[ "name" ] == "origin") + { + level.createfx_last_movement_timer = 0; + frame_selected(); + } + //if we changed angles, restart fx + if( option[ "name" ] == "angles") + { + level.createfx_last_movement_timer = 0; + } + save_redo_buffer(); +} + +set_option_index( name ) +{ + for ( i = 0; i < level._createfx.options.size; i++ ) + { + if ( level._createfx.options[ i ][ "name" ] != name ) + continue; + + level._createfx.selected_fx_option_index = i; + return; + } +} + +get_selected_option() +{ + return level._createfx.options[ level._createfx.selected_fx_option_index ]; +} + +mask( type, name ) +{ + return isdefined( level.createfxMasks[ type ][ name ] ); +} + +addOption( type, name, description, defaultSetting, mask, nowrite, input_func ) +{ + option = []; + option[ "type" ] = type; + option[ "name" ] = name; + option[ "description" ] = description; + option[ "default" ] = defaultSetting; + option[ "mask" ] = mask; + + if ( isdefined(nowrite) && nowrite ) + option[ "nowrite" ] = 1; + else + option[ "nowrite" ] = 0; + + if ( IsDefined( input_func ) ) + { + option[ "input_func" ] = input_func; + } + + level._createfx.options[ level._createfx.options.size ] = option; +} + +get_option( name ) +{ + for ( i = 0; i < level._createfx.options.size; i++ ) + { + if ( level._createfx.options[ i ][ "name" ] == name ) + return level._createfx.options[ i ]; + } +} + +//--------------------------------------------------------- +// Reactive Radius +//--------------------------------------------------------- +input_reactive_radius( menu_index ) +{ + level._createfx.hudelems[ menu_index ][ 0 ] SetDevText( "Reactive Radius, Press: + OR -" ); + + while ( 1 ) + { + wait( 0.05 ); + if ( level.player ButtonPressed( "escape" ) || level.player ButtonPressed( "x" ) ) + break; + + val = 0; + if ( level.player ButtonPressed( "-" ) ) + val = -10; + else if ( level.player ButtonPressed( "=" ) ) + val = 10; + + + if ( val != 0 ) + { + foreach ( ent in level._createfx.selected_fx_ents ) + { + if ( IsDefined( ent.v[ "reactive_radius" ] ) ) + { + ent.v[ "reactive_radius" ] += val; + ent.v[ "reactive_radius" ] = Clamp( ent.v[ "reactive_radius" ], 10, 1000 ); + } + } + } + } + + level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent + update_selected_entities(); + clear_settable_fx(); +} + +//--------------------------------------------------------- +// Display FX Add Options +//--------------------------------------------------------- +display_fx_add_options( ent ) +{ + // are we doing the create fx menu right now? + assert( menu( "add_options" ) ); + assert( entities_are_selected() ); + + level.createfx_menu_list_active = true; + + clear_fx_hudElements(); + set_fx_hudElement( "Name: " + ent.v[ "fxid" ] ); + set_fx_hudElement( "Type: " + ent.v[ "type" ] ); + set_fx_hudElement( "Origin: " + ent.v[ "origin" ] ); + set_fx_hudElement( "Angles: " + ent.v[ "angles" ] ); + + // if entities are selected then we make the entity stats modifiable + count = 0; + drawnCount = 0; + more = false; + + if ( level.effect_list_offset >= level._createfx.options.size ) + level.effect_list_offset = 0; + + for ( i = 0; i < level._createfx.options.size; i++ ) + { + option = level._createfx.options[ i ]; + if ( isdefined( ent.v[ option[ "name" ] ] ) ) + continue; + + // does this type of effect get this kind of option? + if ( !mask( option[ "mask" ], ent.v[ "type" ] ) ) + continue; + + count++ ; + if ( count < level.effect_list_offset ) + continue; + if ( drawnCount >= level.effect_list_offset_max ) + continue; + + drawnCount++ ; + button_to_check = drawnCount; + if ( button_to_check == 10 ) + button_to_check = 0; + if ( button_is_clicked( button_to_check + "" ) ) + { + add_option_to_selected_entities( option ); +// prepare_option_for_change( option, drawnCount ); + menuNone(); + level.last_displayed_ent = undefined; // needed to force a redraw of the last display ent + return; + } + + set_fx_hudElement( button_to_check + ". " + option[ "description" ] ); + } + + if ( count > level.effect_list_offset_max ) + { + level.effect_list_current_size = count; + set_fx_hudElement( "(->) More >" ); + } + + set_fx_hudElement( "(x) Exit >" ); +} + +add_option_to_selected_entities( option ) +{ + setting = undefined; + for ( i = 0; i < level._createfx.selected_fx_ents.size; i++ ) + { + ent = level._createfx.selected_fx_ents[ i ]; + + if ( mask( option[ "mask" ], ent.v[ "type" ] ) ) + ent.v[ option[ "name" ] ] = option[ "default" ]; + } +} + +menuNone() +{ + level.effect_list_offset = 0; + clear_fx_hudElements(); + setMenu( "none" ); +} + +//--------------------------------------------------------- +// Display FX info +//--------------------------------------------------------- +display_fx_info( ent ) +{ + // are we doing the create fx menu right now? + if ( !menu( "none" ) ) + return; + + if ( level.createfx_help_active ) + return; + + clear_fx_hudElements(); + set_fx_hudElement( "Name: " + ent.v[ "fxid" ] ); + set_fx_hudElement( "Type: " + ent.v[ "type" ] ); + + if ( entities_are_selected() ) + { + // if entities are selected then we make the entity stats modifiable + count = 0; + drawnCount = 0; + more = false; + for ( i = 0; i < level._createfx.options.size; i++ ) + { + option = level._createfx.options[ i ]; + if ( !isdefined( ent.v[ option[ "name" ] ] ) ) + continue; + count++ ; + if ( count < level.effect_list_offset ) + continue; + + drawnCount++ ; + set_fx_hudElement( drawnCount + ". " + option[ "description" ] + ": " + ent.v[ option[ "name" ] ] ); + if ( drawnCount > level.effect_list_offset_max ) + { + more = true; + break; + } + } + if ( count > level.effect_list_offset_max ) + { + level.effect_list_current_size = count; + set_fx_hudElement( "(->) More >" ); + } + set_fx_hudElement( "(a) Add >" ); + set_fx_hudElement( "(x) Exit >" ); + } + else + { + count = 0; + more = false; + for ( i = 0; i < level._createfx.options.size; i++ ) + { + option = level._createfx.options[ i ]; + if ( !isdefined( ent.v[ option[ "name" ] ] ) ) + continue; + count++ ; + set_fx_hudElement( option[ "description" ] + ": " + ent.v[ option[ "name" ] ] ); + if ( count > level._createfx.hudelem_count ) + break; + } + } +} + +display_current_translation() +{ + ent = get_last_selected_ent(); + if(isdefined(ent)) + display_fx_info(ent); +} + +//--------------------------------------------------------- +// Draw Effects Section +//--------------------------------------------------------- +draw_effects_list( title ) +{ + clear_fx_hudElements(); + + count = 0; + more = false; + + keys = func_get_level_fx(); + level.effect_list_current_size = keys.size; + + if( !IsDefined( title ) ) + { + title = "Pick an effect"; + } + + set_fx_hudElement( title + " [" + level.effect_list_offset + " - " + keys.size + "]:" ); + +// if ( level.effect_list_offset >= keys.size ) +// level.effect_list_offset = 0; + + for ( i = level.effect_list_offset; i < keys.size; i++ ) + { + count = count + 1; + set_fx_hudElement( count + ". " + keys[ i ] ); + if ( count >= level.effect_list_offset_max ) + { + more = true; + break; + } + } + + if ( keys.size > level.effect_list_offset_max ) + { + set_fx_hudElement( "(->) More >" ); + set_fx_hudElement( "(<-) Previous >" ); + } +} + +increment_list_offset() +{ + if ( level.effect_list_offset >= level.effect_list_current_size - level.effect_list_offset_max ) + { + level.effect_list_offset = 0; + } + else + { + level.effect_list_offset += level.effect_list_offset_max; + } +} + +decrement_list_offset() +{ + if ( level.effect_list_current_size < level.effect_list_offset_max ) + { + level.effect_list_offset = 0; + } + else + { + level.effect_list_offset -= level.effect_list_offset_max; + if ( level.effect_list_offset < 0 ) + { + //keys = func_get_level_fx(); + level.effect_list_offset = level.effect_list_current_size - level.effect_list_offset_max; + } + } +} + +//--------------------------------------------------------- +// Draw Help +//--------------------------------------------------------- +draw_help_list( title ) +{ + clear_fx_hudElements(); + + count = 0; + + keys = level.createfx_help_keys; + + if( !IsDefined( title ) ) + { + title = "Help"; + } + + set_fx_hudElement( "[" + title + "]");// + " [" + level.help_list_offset + " - " + keys.size + "]:" ); + +// if ( level.effect_list_offset >= keys.size ) +// level.effect_list_offset = 0; + + for ( i = level.help_list_offset; i < keys.size; i++ ) + { + count = count + 1; + set_fx_hudElement( keys[ i ] ); + if ( count >= level.help_list_offset_max ) + { + more = true; + break; + } + } + + if ( keys.size > level.help_list_offset_max ) + { + level.effect_list_current_size = keys.size; + set_fx_hudElement( "(->) More >" ); + set_fx_hudElement( "(<-) Previous >" ); + } +} + +increment_help_list_offset() +{ + keys = level.createfx_help_keys; + + if ( level.help_list_offset >= keys.size - level.help_list_offset_max ) + { + level.help_list_offset = 0; + } + else + { + level.help_list_offset += level.help_list_offset_max; + } +} + +decrement_help_list_offset() +{ + level.help_list_offset -= level.help_list_offset_max; + + if ( level.help_list_offset < 0 ) + { + keys = level.createfx_help_keys; + level.help_list_offset = keys.size - level.help_list_offset_max; + } +} + +help_navigation_buttons() +{ + while(level.createfx_help_active == true) + { + if ( next_button() ) + { + increment_help_list_offset(); + draw_help_list(); + wait 0.1; + } + else if ( previous_button() ) + { + decrement_help_list_offset(); + draw_help_list(); + wait 0.1; + } + waitframe(); + } +} + +setup_help_keys() +{ + level.createfx_help_keys = [ + "Insert Insert entity", + "F2 Toggle createfx dot and text drawing", + "F5 SAVES your work", + "Z Undo", + "Shift-Z Redo", + "F Frames currently selected entities in camera view", + "END Drop selected entities to the ground", + "A Add option to the selected entities", + "P Reset the rotation of the selected entities", + "V Copy the angles from the most recently selected fx onto all selected fx.", + "O Orient all selected fx to point at most recently selected fx.", + "S Toggle Snap2Normal mode.", + "L Toggle 90deg Snap mode.", + "G Select all effects in level of same exploder or flag as selected.", + "U Select by name list.", + "C Convert One-Shot to Exploder.", + "Delete Kill the selected entities", + "ESCAPE Cancel out of option-modify-mode, must have console open", + "SPACE or -> Turn on exploders", + "<- Turn off exploders", + "Dpad Move selected entities on X/Y or rotate pitch/yaw", + "A button Toggle the selection of the current entity", + "X button Toggle entity rotation mode", + "Y button Move selected entites up or rotate roll", + "B button Move selected entites down or rotate roll", + "R Shoulder Move selected entities to the cursor", + "L Shoulder Hold to select multiple entites", + "L JoyClick Copy", + "R JoyClick Paste", + "Ctrl-C Copy", + "Ctrl-V Paste", + "N UFO", + "T Toggle Timescale FAST", + "Y Toggle Timescale SLOW", + "[ Toggle FX Visibility", + "] Toggle ShowTris", + "F11 Toggle FX Profile"]; +} + +//--------------------------------------------------------- +// Select by Name Section +//--------------------------------------------------------- +select_by_name() +{ + count = 0; + picked_fx = undefined; + keys = func_get_level_fx(); + + for ( i = level.effect_list_offset; i < keys.size; i++ ) + { + count = count + 1; + button_to_check = count; + if ( button_to_check == 10 ) + button_to_check = 0; + if ( button_is_clicked( button_to_check + "" ) ) + { + picked_fx = keys[ i ]; + break; + } + + if ( count > level.effect_list_offset_max ) + break; + } + + if ( !IsDefined( picked_fx ) ) + return; + + index_array = []; + foreach ( i, ent in level.createFXent ) + { + if ( IsSubStr( ent.v[ "fxid" ], picked_fx ) ) + { + index_array[ index_array.size ] = i; + } + } + + deselect_all_ents(); + select_index_array( index_array ); + + level._createfx.select_by_name = true; +} + +//--------------------------------------------------------- +// Utility Section +//--------------------------------------------------------- +get_last_selected_ent() +{ + return level._createfx.selected_fx_ents[ level._createfx.selected_fx_ents.size - 1 ]; +} \ No newline at end of file diff --git a/raw/common_scripts/_destructible.gsc b/raw/common_scripts/_destructible.gsc new file mode 100644 index 0000000..f400964 --- /dev/null +++ b/raw/common_scripts/_destructible.gsc @@ -0,0 +1,5392 @@ +#include common_scripts\utility; +#using_animtree( "destructibles" ); + +// Car alarm constants +MAX_SIMULTANEOUS_CAR_ALARMS = 2; +CAR_ALARM_ALIAS = "car_alarm"; +CAR_ALARM_OFF_ALIAS = "car_alarm_off"; +NO_CAR_ALARM_MAX_ELAPSED_TIME = 120; +CAR_ALARM_TIMEOUT = 25; +DESTROYED_ATTACHMENT_SUFFIX = "_destroy"; + +SP_DAMAGE_BIAS = 0.5; +SP_EXPLOSIVE_DAMAGE_BIAS = 9.0; + +MP_DAMAGE_BIAS = 1.0; +MP_EXPLOSIVE_DAMAGE_BIAS = 13.0; + +SP_SHOTGUN_BIAS = 8.0; +MP_SHOTGUN_BIAS = 4.0; + +init() +{ + /# + SetDevDvarIfUninitialized( "debug_destructibles", "0" ); + SetDevDvarIfUninitialized( "destructibles_enable_physics", "1" ); + SetDevDvarIfUninitialized( "destructibles_show_radiusdamage", "0" ); + #/ + + level.destructibleSpawnedEntsLimit = 50; + level.destructibleSpawnedEnts = []; + level.currentCarAlarms = 0; + level.commonStartTime = GetTime(); + + if ( isdefined( level.currentgen ) && level.currentgen ) + { + level.destructibleSpawnedEntsLimit = 25; // reduces expense on current gen + } + + if ( !IsDefined( level.fast_destructible_explode ) ) + level.fast_destructible_explode = false; // want to isolate resulting bugs to hamburg. - Nate + + /# + level.created_destructibles = []; + #/ + + if ( !isdefined( level.func ) ) + { + // this array will be filled with code commands that SP or MP may use but doesn't exist in the other. + level.func = []; + } + + destructibles_enabled = true; + /# + destructibles_enabled = ( GetDvarInt( "destructibles_enabled", 1 ) == 1 ); + #/ + + if ( destructibles_enabled ) + find_destructibles(); + + deletables = GetEntArray( "delete_on_load", "targetname" ); + foreach ( ent in deletables ) + ent Delete(); + + init_destroyed_count(); + init_destructible_frame_queue(); +} + + +debgugPrintDestructibleList() +{ + /# + total = 0; + if ( GetDvarInt( "destructibles_locate" ) > 0 ) +{ + // Print out the destructibles we created and where they are all located + PrintLn( "##################" ); + PrintLn( "DESTRUCTIBLE LIST:" ); + PrintLn( "##################" ); + PrintLn( "" ); + + keys = GetArrayKeys( level.created_destructibles ); + foreach ( key in keys ) + { + PrintLn( key + ": " + level.created_destructibles[ key ].size ); + total += level.created_destructibles[ key ].size; + } + PrintLn( "" ); + PrintLn( "Total: " + total ); + PrintLn( "" ); + PrintLn( "Locations:" ); + + foreach ( key in keys ) + { + foreach ( destructible in level.created_destructibles[ key ] ) + { + PrintLn( key + ": " + destructible.origin ); + } + } + + PrintLn( "" ); + PrintLn( "##################" ); + + level.created_destructibles = undefined; + } + #/ +} + + +find_destructibles() +{ + //--------------------------------------------------------------------- + // Find all destructibles by their targetnames and run the setup + //--------------------------------------------------------------------- + + if( !IsDefined( level.destructible_functions ) ) + level.destructible_functions = []; + + // Temporary store any dot refs in prefabs + + dots = []; + + foreach ( struct in level.struct ) + if ( IsDefined( struct.script_noteworthy ) && + struct.script_noteworthy == "destructible_dot" ) + dots[ dots.size ] = struct; + + //assuring orders -nate + vehicles = GetEntArray( "destructible_vehicle", "targetname" ); + foreach ( vehicle in vehicles ) + { + vehicle thread setup_destructibles_thread( dots ); + } + + destructible_toy = GetEntArray( "destructible_toy", "targetname" ); + foreach ( toy in destructible_toy ) + { + toy thread setup_destructibles_thread( dots ); + } + + debgugPrintDestructibleList(); +} + + +setup_destructibles_thread( dots ) +{ + self setup_destructibles(); + self setup_destructible_dots( dots ); +} + + +setup_destructible_dots( dots ) +{ + destructibleInfo = self.destructibleInfo; + + AssertEx( IsDefined( destructibleInfo ), "Setup destructibles did not execute properly for this destructible" ); + + foreach ( dot in dots ) + { + if ( IsDefined( level.destructible_type[ destructibleInfo ].destructible_dots ) ) + return; + + if ( IsDefined( dot.script_parameters ) && + IsSubStr( dot.script_parameters, "destructible_type" ) && + IsSubStr( dot.script_parameters, self.destructible_type ) ) + { + // This isnt the best solution. With how are prefabs system works I basically look for the dot reference closest to + // the destructible origin + + if ( DistanceSquared( self.origin, dot.origin ) < 1 ) + { + triggers = GetEntArray( dot.target, "targetname" ); + level.destructible_type[ destructibleInfo ].destructible_dots = []; + + // Find out which triggers are associated with this destructible and "precache" the info + + foreach ( trigger in triggers ) + { + script_index = trigger.script_index; + + AssertEx( IsDefined( script_index ), "Must specify a script_index for trigger being used as DOT in destructible prefab" ); + + if ( !IsDefined( level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ] ) ) + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ] = []; + + triggerIndex = level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ].size; + + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ][ triggerIndex ][ "classname" ] = trigger.classname; + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ][ triggerIndex ][ "origin" ] = trigger.origin; + spawnflags = ter_op( IsDefined( trigger.spawnflags ), trigger.spawnflags, 0 ); + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ][ triggerIndex ][ "spawnflags" ] = spawnflags; + + switch( trigger.classname ) + { + case "trigger_radius": + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ][ triggerIndex ][ "radius" ] = trigger.height; + level.destructible_type[ destructibleInfo ].destructible_dots[ script_index ][ triggerIndex ][ "height" ] = trigger.height; + break; + default: + AssertMsg( "This class is not supported for DOTs" ); + } + trigger Delete(); + } + break; + } + } + } +} + + +destructible_getInfoIndex( destructibleType ) +{ + if ( !isdefined( level.destructible_type ) ) + return - 1; + if ( level.destructible_type.size == 0 ) + return - 1; + + for ( i = 0 ; i < level.destructible_type.size ; i++ ) + { + if ( destructibleType == level.destructible_type[ i ].v[ "type" ] ) + return i; + } + + // didn't find it in the array, must not exist + return - 1; +} + + +dest_cover( destructibleName, numchunks, chunk_efxname, chunkhealth, damaged_model, chunk_soundalias ) +{ + //parse arguments + if( !IsDefined( numchunks ) ) numchunks = 0; + if( !IsDefined( chunk_efxname ) ) chunk_efxname = "test/concrete_cover_dest_test"; + if( !IsDefined( chunkhealth ) ) chunkhealth = 150; + + //create the main destructible + destructible_create( destructibleName, "tag_origin", 1, undefined, 32, "no_melee" ); + if( IsDefined( damaged_model ) ) + destructible_state( undefined, damaged_model, undefined, undefined, 32, "no_melee" ); + for( i = 0; i < numchunks; i++ ) + { + //automatically create the parts for destructible cover + tag_name = "fx_joint_" + i; + //println(tag_name); + destructible_part( tag_name, undefined, chunkhealth, undefined, undefined, "no_melee", true); + destructible_fx( tag_name, chunk_efxname); + if( IsDefined( chunk_soundalias ) ) + destructible_sound( chunk_soundalias ); + destructible_state( undefined ); + } +} + + +destructible_getType( destructibleType ) +{ + // if it's already been created dont create it again + infoIndex = destructible_getInfoIndex( destructibleType ); + if ( infoIndex >= 0 ) + return infoIndex; + + if ( IsSubStr( destructibleType, "dest_cover" ) ) + { + dest_cover( self.destructible_type, self.script_dest_cover_numchunks, self.script_dest_cover_chunkfx, self.script_dest_cover_chunkhealth, self.script_dest_cover_dmg_model, self.script_dest_cover_chunksnd ); + infoIndex = destructible_getInfoIndex( destructibleType ); + assert( infoIndex >= 0 ); + return infoIndex; + } + + // This is the new stuff, Each Script describes this function. + if ( !IsDefined( level.destructible_functions[ destructibleType ] ) ) + { + println( "^1WARNING: Destructible object 'destructible_type' " + destructibleType + "' is not valid. Have you Repackaged Zone/Script? Sometimes you need to rebuild BSP ents." ); + return -1; // NER + // AssertMsg( "Destructible object 'destructible_type' " + destructibleType + "' is not valid. Have you Repackaged Zone/Script? Sometimes you need to rebuild BSP ents." ); + } + + [[ level.destructible_functions[ destructibleType ] ]](); + infoIndex = destructible_getInfoIndex( destructibleType ); + assert( infoIndex >= 0 ); + return infoIndex; +} + + +setup_destructibles() +{ + //--------------------------------------------------------------------- + // Figure out what destructible information this entity should use + //--------------------------------------------------------------------- + destructibleInfo= undefined; + AssertEx( IsDefined( self.destructible_type ), "Destructible object with targetname 'destructible' does not have a 'destructible_type' key / value" ); + + self.modeldummyon = false;// - nate added for vehicle dummy stuff. This is so I can turn a destructible into a dummy and throw it around on jeepride. + self add_damage_owner_recorder(); // Mackey added to track who is damaging the car + + self.destructibleInfo = destructible_getType( self.destructible_type ); + if ( self.destructibleInfo< 0 ) + return; + + /# + // Store what destructibles we create and where they are located so we can get a list in the console + if ( !isdefined(level.created_destructibles) ) + level.created_destructibles = []; + if ( !isdefined( level.created_destructibles[ self.destructible_type ] ) ) + level.created_destructibles[ self.destructible_type ] = []; + nextIndex = level.created_destructibles[ self.destructible_type ].size; + level.created_destructibles[ self.destructible_type ][ nextIndex ] = self; + #/ + + precache_destructibles(); + + add_destructible_fx(); + + if ( IsDefined( level.destructible_transient ) && IsDefined( level.destructible_transient[ self.destructible_type ] ) ) + { + flag_wait( level.destructible_transient[ self.destructible_type ] + "_loaded" ); + } + + + + // Boon Oct2012. Allow models to be attached, so we don't have to export models with complex intact and destroyed variations all in one. + // This should save some memory in cases where we also want static intact and/or destroyed models, since they can now reuse geo. + if ( IsDefined( level.destructible_type[ self.destructibleInfo].attachedModels ) ) + { + foreach ( attachedModel in level.destructible_type[ self.destructibleInfo].attachedModels ) + { + if ( IsDefined( attachedModel.tag ) ) + self Attach( attachedModel.model, attachedModel.tag ); + else + self Attach( attachedModel.model ); + if ( self.modeldummyon ) + if ( IsDefined( attachedModel.tag ) ) + self.modeldummy Attach( attachedModel.model, attachedModel.tag ); + else + self.modeldummy Attach( attachedModel.model ); + } + } + + //--------------------------------------------------------------------- + // Set up all parts on the entity + //--------------------------------------------------------------------- + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts ) ) + { + self.destructible_parts = []; + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + // create the struct where the info for each entity will be held + self.destructible_parts[ i ] = SpawnStruct(); + + // set it's current state to 0 since it has never taken damage yet and will be on it's first state + self.destructible_parts[ i ].v[ "currentState" ] = 0; + + // if it has a health value then store it's value + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "health" ] ) ) + self.destructible_parts[ i ].v[ "health" ] = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "health" ]; + + // find random attachements such as random advertisements on taxi cabs and attach them now + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ] ) ) + { + randAttachmentIndex = RandomInt( level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ].size ); + attachTag = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "random_dynamic_attachment_tag" ][ randAttachmentIndex ]; + attach_model_1 = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "random_dynamic_attachment_1" ][ randAttachmentIndex ]; + attach_model_2 = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "random_dynamic_attachment_2" ][ randAttachmentIndex ]; + clipToRemove = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "clipToRemove" ][ randAttachmentIndex ]; + self thread do_random_dynamic_attachment( attachTag, attach_model_1, attach_model_2, clipToRemove ); + } + + // continue if it's the base model since its not an attached part + if ( i == 0 ) + continue; + + // (Boon Aug2012: The modelName stuff here appears to be dead code, left over from when parts were actually attached.) + // Show and hide parts as appropriate + modelName = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "modelName" ]; + tagName = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "tagName" ]; + + stateIndex = 1; + while ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ] ) ) + { + stateTagName = level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "tagName" ]; + stateModelName = level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "modelName" ]; + if ( IsDefined( stateTagName ) && stateTagName != tagName ) + { + self hideapart( stateTagName ); + if ( self.modeldummyon ) + self.modeldummy hideapart( stateTagName ); + } + stateIndex++; + } + } + } + + // some destructibles have collision that needs to change due to the large change in the destructible when it blows up + if ( IsDefined( self.target ) ) + thread destructible_handles_collision_brushes(); + + //--------------------------------------------------------------------- + // Make this entity take damage and wait for events + //--------------------------------------------------------------------- + if ( self.code_classname != "script_vehicle" ) + self SetCanDamage( true ); + if ( isSP() ) + self thread connectTraverses(); + self thread destructible_think(); + + if ( IsSubStr( self.destructible_type, "dest_cover" ) ) + { + self thread destructibleCoverWatcher(); //Initialize thread that watches distance of player to destructible to enable/disable setdamage + } + + self thread destructible_fx_spawnImmediate(); // Launch any fx that need to spawn immediately +} + +/* +============= +///ScriptDocBegin +"Name: destructible_create( , , , , , )" +"Summary: Define a new type of destructible. This automatically creates the first 'part' in the destructible and the first 'state' for that part." +"Module: Destructible" +"CallOn: nothing" +"MandatoryArg: : Each type of destructible needs a unique name. This is generally the same as the name of the gsc file and must match the 'destructible_type' keypair in Radiant." +"MandatoryArg: : It is essential that this tag exists but I haven't yet figured out if there's a good reason for it." +"MandatoryArg: : When this much damage is done, the destructible will do everything defined by the destructible_fx, destructible_anim, etc functions you put below this one, and transition to the next state." +"OptionalArg: : See isAttackerValid() for good values." +"OptionalArg: : This is always 32. As far as I can tell it's not used for anything." +"OptionalArg: : See isValidDamageCause() for good values. Undefined means all damage is valid." +"Example: destructible_create( "vehicle_pickup", "tag_body", 300, undefined, 32, "no_melee" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_create( type, tagName, health, validAttackers, validDamageZone, validDamageCause ) +{ + //--------------------------------------------------------------------- + // Creates a new information structure for a destructible object + //--------------------------------------------------------------------- + Assert( IsDefined( type ) ); + + if ( !isdefined( level.destructible_type ) ) + level.destructible_type = []; + + destructibleIndex = level.destructible_type.size; + level.destructible_type[ destructibleIndex ] = SpawnStruct(); + level.destructible_type[ destructibleIndex ].v[ "type" ] = type; + + level.destructible_type[ destructibleIndex ].parts = []; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ] = SpawnStruct(); + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "modelName" ] = self.model; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "tagName" ] = tagName; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "health" ] = health; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validAttackers" ] = validAttackers; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validDamageZone" ] = validDamageZone; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "validDamageCause" ] = validDamageCause; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "godModeAllowed" ] = true; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "rotateTo" ] = self.angles; + level.destructible_type[ destructibleIndex ].parts[ 0 ][ 0 ].v[ "vehicle_exclude_anim" ] = false; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_part( , , , , , , , , , )" +"Summary: Add a new 'part' to the last destructible type created. A part can be either a piece of the model that can be damaged (and hidden, shown, etc) separately from the rest, or a separate model that can be thrown using physics." +"Module: Destructible" +"CallOn: nothing" +"MandatoryArg: : All geometry skinned to this tag will be considered to be this part." +"OptionalArg: : The model fired off. This happens on a state change (if destructible_physics() is specified) or when the destructible explodes. The model is fired from "tagName", directly away from the origin of the entity." +"OptionalArg: : How much damage this part can sustain before transitioning to its next state. Undefined means this part does not take damage." +"OptionalArg: : See isAttackerValid() for good values." +"OptionalArg: : This is always 32. As far as I can tell it's not used for anything though." +"OptionalArg: : See isValidDamageCause() for good values. Undefined means all damage is valid." +"OptionalArg: : Fraction of damage done to this part that is also passed on to the parent entity. Defaults to 0." +"OptionalArg: : If set >0 and a state transition happens where destructible_explode() is used, launch "modelName" using physics with velocity scaled by this value." +"OptionalArg: : If defined, transition to the next state if hit by a grenade. Good for windows." +"OptionalArg: : Fraction of damage done to parent that will be passed on to this part. Must be >0. Defaults to 0. Appears to also receive damage from other child parts in the model." +"Example: destructible_part( "tag_hood", "vehicle_pickup_hood", 800, undefined, undefined, undefined, 1.0, 2.5 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_part( tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, receiveDamageFromParent ) +{ + //--------------------------------------------------------------------- + // Adds a part to the last created destructible information structure + //--------------------------------------------------------------------- + destructibleIndex = ( level.destructible_type.size - 1 ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts.size ) ); + + partIndex = level.destructible_type[ destructibleIndex ].parts.size; + Assert( partIndex > 0 ); + + stateIndex = 0; + + destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, undefined, receiveDamageFromParent ); +} + +/* +============= +///ScriptDocBegin +"Name: destructible_state( , , , , , , , )" +"Summary: Add a new 'state' to the last destructible part created. When the previous state reaches zero health, this state will be entered." +"Module: Destructible" +"CallOn: Nothing" +"OptionalArg: : Any geometry skinned to this tag will be shown when in this state, and hidden otherwise (unless another state uses it)." +"OptionalArg: : If this is the base part, model to swap the entity's model to when in this state (if undefined, previous model will be left alone). If non-base part, (unconfirmed!) model to be thrown using physics if the destructible explodes while this part is in this state." +"OptionalArg: : How much damage this part can sustain before transitioning to its next state. Undefined means do not transition." +"OptionalArg: : See isAttackerValid() for good values." +"OptionalArg: : This is always 32. As far as I can tell it's not used for anything though." +"OptionalArg: : See isValidDamageCause() for good values. Undefined means all damage is valid." +"OptionalArg: : If defined, transition to the next state if hit by a grenade. Good for windows." +"OptionalArg: : Rotate the model to face the damage when entering this state. For example, this is used by toy_mailbox2_yellow to make it bend away from the damage." +"Example: destructible_state( undefined, "vehicle_pickup_destroyed", undefined, 32, "no_melee" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_state( tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, grenadeImpactDeath, splashRotation ) +{ + //--------------------------------------------------------------------- + // Adds a new part that is a state of the last created part + // When the previous part reaches zero health this part will show up + // and the previous part will be removed + //--------------------------------------------------------------------- + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size ); + + if ( !isdefined( tagName ) && partIndex == 0 ) + tagName = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ 0 ].v[ "tagName" ]; + + destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, undefined, undefined, grenadeImpactDeath, splashRotation ); +} + +/* +============= +///ScriptDocBegin +"Name: destructible_fx_spawn_immedate( , , , , , , , < stateChangeKill > )" +"Summary: Play FX after the health of the previously defined destructible state reaches zero. Same as destructible_fx except that +the spawn_immedate flag is always set to true. Used explicitly by the destructible UI tool." +"Model: Destructible" +"CallOn: Nothing" +"OptionalArg: : Tag to play the effects from. If undefined, play from the entity's origin, facing up." +"MandatoryArg: : FX file to play, with path relative to game\share\raw directory, and no file extension." +"OptionalArg: : If true or undefined, orient FX to tag's angles. If false, face FX up." +"OptionalArg: : Only play this FX if the last state transition was caused by a damage type that is a substring of this string. The function damage_not() is interesting here." +"OptionalArg: : If there are multiple groups defined in a state, a random group is chosen when the state is exited and only the FX, animations and sounds for that group are played. Toy_locker_double is the only example I have found." +"OptionalArg: : If defined (to a number, generally 1), prevents too many destructibles entering a state on the same frame, ie prevents too many FX being started on the same frame." +"OptionalArg: : Launch the fx immediately regarless of the state." +"OptionalArg: : When set to one, the fx will soft kill on the next state change - or - when the destructible reaches its final state." +"Example: destructible_fx( "tag_death_fx", "fx/explosions/small_vehicle_explosion", false );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_fx_spawn_immediate( tagName, fxName, useTagAngles, damageType, groupNum, fxCost, stateChangeKill ) +{ + destructible_fx( tagName, fxName, useTagAngles, damageType, groupNum, fxCost, true, stateChangeKill ); +} + + +/* +============= +///ScriptDocBegin +"Name: destructible_fx( , , , , , , , < stateChangeKill > )" +"Summary: Play FX after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: Nothing" +"OptionalArg: : Tag to play the effects from. If undefined, play from the entity's origin, facing up." +"MandatoryArg: : FX file to play, with path relative to game\share\raw directory, and no file extension." +"OptionalArg: : If true or undefined, orient FX to tag's angles. If false, face FX up." +"OptionalArg: : Only play this FX if the last state transition was caused by a damage type that is a substring of this string. The function damage_not() is interesting here." +"OptionalArg: : If there are multiple groups defined in a state, a random group is chosen when the state is exited and only the FX, animations and sounds for that group are played. Toy_locker_double is the only example I have found." +"OptionalArg: : If defined (to a number, generally 1), prevents too many destructibles entering a state on the same frame, ie prevents too many FX being started on the same frame." +"OptionalArg: : Launch the fx immediately regarless of the state." +"OptionalArg: : When set to one or two, the fx will soft kill or hard kill respectively ( hardkill only works if "usetagangles" is enabled ) on the next state change - or - when the destructible reaches its final state." +"Example: destructible_fx( "tag_death_fx", "fx/explosions/small_vehicle_explosion", false );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_fx( tagName, fxName, useTagAngles, damageType, groupNum, fxCost, spawnImmediate, stateChangeKill ) +{ + //assert( IsDefined( tagName ) ); + Assert( IsDefined( fxName ) ); + + if ( !isdefined( useTagAngles ) ) + useTagAngles = true; + + if ( !isdefined( groupNum ) ) + groupNum = 0; + + if ( !isdefined( fxCost ) ) + fxCost = 0; + + if ( !isdefined( spawnImmediate ) ) + spawnImmediate = false; + + if ( !isdefined( stateChangeKill ) ) + stateChangeKill = 0; + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + fx_size = 0; + if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ] ) ) + if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ] ) ) + fx_size = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ].size; + + if ( IsDefined( damageType ) ) + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_valid_damagetype" ][ groupNum ][ fx_size ] = damageType; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_filename" ][ groupNum ][ fx_size ] = fxName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_tag" ][ groupNum ][ fx_size ] = tagName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_useTagAngles" ][ groupNum ][ fx_size ] = useTagAngles; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "fx_cost" ][ groupNum ][ fx_size ] = fxCost; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "spawn_immediate" ][ groupNum ][ fx_size ] = spawnImmediate; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "state_change_kill" ][ groupNum ][ fx_size ] = stateChangeKill; + +} + +// Boon August2012: DOT is Damage Over Time? Like poison gas? +destructible_createDOT_predefined( index ) +{ + AssertEx( IsDefined( index ), "Must specify an index >= 0" ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ] ) ) + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ] = []; + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size ); + dot = createDOT(); + dot.type = "predefined"; + dot.index = index; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ] = dot; +} + +destructible_createDOT_radius( tag, spawnflags, radius, height ) +{ + AssertEx( IsDefined( tag ), "Must define tag where dot with be spawned for destructible" ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ] ) ) + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ] = []; + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size ); + dot = createDOT_radius( ( 0, 0, 0 ), spawnflags, radius, height ); + dot.tag = tag; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ] = dot; +} + +destructible_setDOT_onTick( delay, interval, duration, minDamage, maxDamage, falloff, type, affected ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + dot setDOT_onTick( delay, interval, duration, minDamage, maxDamage, falloff, type, affected ); + initDOT( type ); +} + +destructible_setDOT_onTickFunc( onEnterFunc, onExitFunc, onDeathFunc ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + tickIndex = dot.ticks.size; + + dot.ticks[ tickIndex ].onEnterFunc = onEnterFunc; + dot.ticks[ tickIndex ].onExitFunc = onExitFunc; + dot.ticks[ tickIndex ].onDeathFunc = onDeathFunc; +} + +destructible_buildDOT_onTick( duration, affected ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + dot buildDOT_onTick( duration, affected ); +} + +destructible_buildDOT_startLoop( count ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + dot buildDOT_startLoop( count ); +} + +destructible_buildDOT_damage( minDamage, maxDamage, falloff, damageFlag, meansOfDeath, weapon ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + dot buildDOT_damage( minDamage, maxDamage, falloff, damageFlag, meansOfDeath, weapon ); +} + +destructible_buildDOT_wait( time ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + dotIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ].size - 1 ); + dot = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "dot" ][ dotIndex ]; + + Assert( IsDefined( dot ) ); + + dot buildDOT_wait( time ); +} + +/* +============= +///ScriptDocBegin +"Name: destructible_loopfx( , , , )" +"Summary: Play FX over and over after the health of the previously defined destructible state reaches zero. Calls loopfx_onTag()" +"Module: Destructible" +"CallOn: Nothing" +"OptionalArg: : Tag to play the effects from. If undefined, play from the entity's origin, facing up." +"MandatoryArg: : FX file to play, with path relative to game\share\raw directory." +"OptionalArg: : Time to wait between FX." +"OptionalArg: : If defined (to a number, generally 1), prevents too many destructibles entering a state on the same frame, ie prevents too many FX being started on the same frame." +"Example: destructible_loopfx( "tag_hood_fx", "fx/smoke/car_damage_blacksmoke", 0.4 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_loopfx( tagName, fxName, loopRate, fxCost ) +{ + Assert( IsDefined( tagName ) ); + Assert( IsDefined( fxName ) ); + Assert( IsDefined( loopRate ) ); + Assert( loopRate > 0 ); + + if ( !isdefined( fxCost ) ) + fxCost = 0; + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + fx_size = 0; + if ( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ] ) ) + fx_size = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ].size; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_filename" ][ fx_size ] = fxName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_tag" ][ fx_size ] = tagName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_rate" ][ fx_size ] = loopRate; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopfx_cost" ][ fx_size ] = fxCost; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_healthdrain( , , , )" +"Summary: Drain health from this entity or part while in this state. Note that health drain continues in following states unless changed by another call to this function. See function health_drain() for actual workings." +"Module: Destructible" +"CallOn: An entity" +"MandatoryArg: : Amount of damage to do." +"MandatoryArg: : Interval between damage, in seconds." +"OptionalArg: : Radius of the bad place cylinder to create. nb The cylinder is always 128 units high." +"OptionalArg: : Any valid "team" parameter for the code function BadPlace_Cylinder(), or "both", which is translated to mean "allies" and "bad_guys"." +"Example: destructible_healthdrain( 15, 0.25, 210, "allies" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_healthdrain( amount, interval, badplaceRadius, badplaceTeam ) +{ + Assert( IsDefined( amount ) ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "healthdrain_amount" ] = amount; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "healthdrain_interval" ] = interval; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "badplace_radius" ] = badplaceRadius; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "badplace_team" ] = badplaceTeam; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_sound( , , )" +"Summary: Play sound after the health of the previously defined destructible state reaches zero. Sound is played at the tag defined for the previously defined part/state." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : Sound alias!" +"OptionalArg: : If defined, this sound will only be played if soundCause matches the damage type." +"OptionalArg: : If there are multiple groups defined in a state, a random group is chosen when the state is exited and only the FX, animations and sounds for that group are played. Toy_locker_double is the only example I have found." +"Example: destructible_sound( "veh_tire_deflate", "bullet" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_sound( soundAlias, soundCause, groupNum ) +{ + Assert( IsDefined( soundAlias ) ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !isdefined( groupNum ) ) + groupNum = 0; + + if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ] ) ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ] = []; + } + + if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ] ) ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ][ groupNum ] = []; + } + + index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ].size; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "sound" ][ groupNum ][ index ] = soundAlias; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "soundCause" ][ groupNum ][ index ] = soundCause; +} + + +/* +============= +///ScriptDocBegin +"Name: destructible_loopsound( , , )" +"Summary: Play looping sound after the health of the previously defined destructible state reaches zero. Sound is played at the tag defined for the previously defined part/state." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : Sound alias!" +"OptionalArg: : If defined, this sound will only be played if loopsoundCause matches the damage type." +"Example: destructible_loopsound( "fire_vehicle_med" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_loopsound( soundAlias, loopsoundCause ) +{ + Assert( IsDefined( soundAlias ) ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ] ) ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsoundCause" ] = []; + } + + index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ].size; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsound" ][ index ] = soundAlias; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "loopsoundCause" ][ index ] = loopsoundCause; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_anim( , , , , , , , , )" +"Summary: Play animation after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : The animation to play." +"MandatoryArg: : The animtree that the animation belongs to. Commonly the animtree is set at the top of the script with "#using_animtree" and "#animtree" is used here." +"MandatoryArg: : The type of anim call to make, either "setanim" (to play this animation and leave others alone) or "setanimknob" (to play this animation and stop playing others)." +"OptionalArg: : If set to true, don't play this animation on vehicles in multiplayer?" +"OptionalArg: : If there are multiple groups defined in a state, a random group is chosen when the state is exited and only the FX, animations and sounds for that group are played. Toy_locker_double is the only example I have found." +"OptionalArg: : The animation to play, as a string, for MP" +"OptionalArg: : Randomly wait up to this long (in seconds) before starting the animation." +"OptionalArg: : Randomly scale animation playback speed to a value between this and animRateMax." +"OptionalArg: : Randomly scale animation playback speed to a value between animRateMin and this." +"Example: destructible_anim( %vehicle_80s_sedan1_destroy, #animtree, "setanimknob", undefined, undefined, "vehicle_80s_sedan1_destroy" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_anim( animName, animTree, animType, vehicle_exclude, groupNum, mpAnim, maxStartDelay, animRateMin, animRateMax ) +{ + if ( !isdefined( vehicle_exclude ) ) + vehicle_exclude = false; + + Assert( IsDefined( anim ) ); + Assert( IsDefined( animName ) ); + Assert( IsDefined( animtree ) ); + + if ( !isdefined( groupNum ) ) + groupNum = 0; + + array = []; + array[ "anim" ] = animName; + array[ "animTree" ] = animtree; + array[ "animType" ] = animType; + array[ "vehicle_exclude_anim" ] = vehicle_exclude; + array[ "groupNum" ] = groupNum; + array[ "mpAnim" ] = mpAnim; + array[ "maxStartDelay" ] = maxStartDelay; + array[ "animRateMin" ] = animRateMin; + array[ "animRateMax" ] = animRateMax; + add_array_to_destructible( "animation", array ); +} + +/* +============= +///ScriptDocBegin +"Name: destructible_spotlight( )" +"Summary: Attach a spotlight to a tag, eg when a hanging light is shot." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : " +"Example: destructible_spotlight( "tag_swing_r_far" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_spotlight( tag ) +{ + AssertEx( IsDefined( tag ), "Tag wasn't defined for destructible_spotlight" ); + array = []; + array[ "spotlight_tag" ] = tag; + array[ "spotlight_fx" ] = "spotlight_fx"; + array[ "spotlight_brightness" ] = 0.85; + array[ "randomly_flip" ] = true; + + add_keypairs_to_destructible( array ); +} + +add_key_to_destructible( key, val ) +{ + AssertEx( IsDefined( key ), "Key wasn't defined!" ); + AssertEx( IsDefined( val ), "Val wasn't defined!" ); + + array = []; + array[ key ] = val; + add_keypairs_to_destructible( array ); +} + +/* +============= +///ScriptDocBegin +"Name: add_keypairs_to_destructible( )" +"Summary: Goes through the array and adds each key/val to .v of the most recently defined state." +"Module: Destructibles" +"MandatoryArg: : Array of keypairs." +"Example: add_keypairs_to_destructible( array );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +add_keypairs_to_destructible( array ) +{ + // add a single flat array to the destructible, overwriting any existing identical keys. + destructibleIndex = level.destructible_type.size - 1; + partIndex = level.destructible_type[ destructibleIndex ].parts.size - 1; + stateIndex = level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1; + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + foreach ( key, val in array ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ key ] = val; + } +} + +/* +============= +///ScriptDocBegin +"Name: add_array_to_destructible( , )" +"Summary: Adds the array to the end of .v[ array_name ]" +"Module: Destructibles" +"MandatoryArg: : Name of array index in .v. May already exist; if so, array will be appended (not concatenated) to it." +"MandatoryArg: : Array of keypairs to be added." +"Example: add_array_to_destructible( array );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +add_array_to_destructible( array_name, array ) +{ + // add an array under a key name, so you can have multiple arrays under a given key name + destructibleIndex = level.destructible_type.size - 1; + partIndex = level.destructible_type[ destructibleIndex ].parts.size - 1; + stateIndex = level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1; + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + v = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v; + + if ( !isdefined( v[ array_name ] ) ) + { + v[ array_name ] = []; + } + + v[ array_name ][ v[ array_name ].size ] = array; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v = v; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_car_alarm()" +"Summary: Play a car alarm sound after the health of the previously defined destructible state reaches zero. Calls thread do_car_alarm()." +"Module: Destructible" +"CallOn: Nothing" +"Example: destructible_car_alarm();" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_car_alarm() +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "triggerCarAlarm" ] = true; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_lights_out( )" +"Summary: Switches off the nearest light if it is within range, after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : Maximum range in units to look for the light." +"Example: destructible_lights_out( 16 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_lights_out( range ) +{ + if ( !isdefined( range ) ) + range = 256; + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "break_nearby_lights" ] = range; +} + +// made so I can put random advertisements on the destructible taxi cabs without making lots of destructible types for each version +random_dynamic_attachment( tagName, attachment_1, attachment_2, clipToRemove ) +{ + + Assert( IsDefined( tagName ) ); + Assert( IsDefined( attachment_1 ) ); + + if ( !isdefined( attachment_2 ) ) + attachment_2 = ""; + + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + //stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + stateIndex = 0; + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ] ) ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_2" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_tag" ] = []; + } + + index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ].size; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_1" ][ index ] = attachment_1; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_2" ][ index ] = attachment_2; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "random_dynamic_attachment_tag" ][ index ] = tagName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "clipToRemove" ][ index ] = clipToRemove; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_physics( , )" +"Summary: Throw this part's model using physics, after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: An entity" +"OptionalArg: : Tag of other part to throw using physics (useful when called on base part)." +"OptionalArg: : Vector velocity, in the space of the tag. eg use (200,0,0) to fire along the tag's forward axis. Defaults to throwing away from impact." +"Example: destructible_physics( "tag_cap", ( 50, 0, 0 ) );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_physics( physTagName, physVelocity ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( !isdefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ] ) ) + { + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_tagName" ] = []; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_velocity" ] = []; + } + + index = level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ].size; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics" ][ index ] = true; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_tagName" ][ index ] = physTagName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physics_velocity" ][ index ] = physVelocity; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_splash_damage_scaler( )" +"Summary: Scale splash damage done to destructibles of this type. Only works when called before any additional parts or states are defined." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: " +"Example: destructible_splash_damage_scaler( 15 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_splash_damage_scaler( damage_multiplier ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "splash_damage_scaler" ] = damage_multiplier; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_explode( , , , , , , , , , , , , , )" +"Summary: Create an explosion that throws physics parts and does splash damage to nearby objects, after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : Initial velocity of each thrown part will be multiplied by a random amount between this and force_max." +"MandatoryArg: : Initial velocity of each thrown part will be multiplied by a random amount between force_min and this." +"MandatoryArg: : Max distance over which objects are damaged by explosion, in SP." +"MandatoryArg: : Max distance over which objects are damaged by explosion, in MP." +"MandatoryArg: : Damage done to objects at maximum distance." +"MandatoryArg: : Damage done to objects at zero distance." +"OptionalArg: : Allow this destructible to continue taking damage after the explosion." +"OptionalArg: : Vertical offset for explosion center. Added to originOffset3D. Defaults to 80." +"OptionalArg: : Create a 2 second earthquake with this scale." +"OptionalArg: : Create a 2 second earthquake with this radius." +"OptionalArg: : 3D offset for explosion center. Added to originOffset. Defaults to (0,0,0)." +"OptionalArg: : Wait before triggering the explosion." +"OptionalArg: : Initial angular impulse applied to each thrown part with a magnitude between this and angularImpulse_max. Setting angularImpulse_min and angularImpulse_max also means that force_min and force_max get treated as impulse units and not velocity." +"OptionalArg: : Initial angular impulse applied to each thrown part with a magnitude between angularImpulse_min and this. Setting angularImpulse_min and angularImpulse_max also means that force_min and force_max get treated as impulse units and not velocity." +"Example: destructible_explode( 4000, 5000, 210, 250, 50, 300, undefined, undefined, 0.3, 500 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_explode( force_min, force_max, rangeSP, rangeMP, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, originOffset3d, delaytime, angularImpulse_min, angularImpulse_max ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + if ( isSP() ) + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_range" ] = rangeSP; + else + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_range" ] = rangeMP; + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode" ] = true; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_force_min" ] = force_min; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_force_max" ] = force_max; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_mindamage" ] = mindamage; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_maxdamage" ] = maxdamage; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "continueDamage" ] = continueDamage; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "originOffset" ] = originOffset; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "earthQuakeScale" ] = earthQuakeScale; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "earthQuakeRadius" ] = earthQuakeRadius; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "originOffset3d" ] = originOffset3d; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "delaytime" ] = delaytime; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_angularImpulse_min" ] = angularImpulse_min; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "explode_angularImpulse_max" ] = angularImpulse_max; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_function( , )" +"Summary: Call the specified function on the entity, after the health of the previously defined destructible state reaches zero." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : A pointer to the function to be called" +"Example: destructible_function( ::removeScreen );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_function( functionPtr ) +{ + Assert( IsDefined( level.destructible_type ) ); + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "function" ] = functionPtr; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_notify( )" +"Summary: Notify the supplied string on the entity when the current part leaves this state." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : String to notify" +"Example: destructible_notify( "stop flashing" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_notify( notifyStr ) +{ + Assert( IsDefined( level.destructible_type ) ); + destructibleIndex = ( level.destructible_type.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "functionNotify" ] = notifyStr; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_damage_threshold( )" +"Summary: Minimum damage that will have any effect on the health of this state. If damage done in one hit is less than this amount, it will be ignored." +"Module: Destructible" +"CallOn: Nothing" +"MandatoryArg: : A number" +"Example: destructible_damage_threshold( 50 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +destructible_damage_threshold( damage_threshold ) +{ + destructibleIndex = ( level.destructible_type.size - 1 ); + partIndex = ( level.destructible_type[ destructibleIndex ].parts.size - 1 ); + stateIndex = ( level.destructible_type[ destructibleIndex ].parts[ partIndex ].size - 1 ); + + Assert( IsDefined( level.destructible_type ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ] ) ); + Assert( IsDefined( level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] ) ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "damage_threshold" ] = damage_threshold; +} + +/* +============= +///ScriptDocBegin +"Name: destructible_attachmodel( , )" +"Summary: Attach a model to this destructible, fusing them together to create a single entity." +"Module: Destructible" +"CallOn: Nothing." +"OptionalArg: : Tag to attach the model to. If undefined, model will be merged with base model." +"MandatoryArg: : Model to attach." +"Example: destructible_attachmodel( undefined, "lv_slot_machine_destroyed" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +destructible_attachmodel( tagName, modelName ) +{ + Assert( IsDefined( modelName ) ); + Assert( IsDefined( level.destructible_type ) ); + Assert( level.destructible_type.size > 0 ); + modelName = ToLower( modelName ); + destructibleIndex = ( level.destructible_type.size - 1 ); + + if ( !IsDefined( level.destructible_type[ destructibleIndex ].attachedModels ) ) + level.destructible_type[ destructibleIndex ].attachedModels = []; + attachedModel = SpawnStruct(); + attachedModel.model = modelName; + attachedModel.tag = tagName; + level.destructible_type[ destructibleIndex ].attachedModels[ level.destructible_type[ destructibleIndex ].attachedModels.size ] = attachedModel; +} + +/* +"Worker function called by destructible_part() and destructible_state()." +*/ + +destructible_info( partIndex, stateIndex, tagName, modelName, health, validAttackers, validDamageZone, validDamageCause, alsoDamageParent, physicsOnExplosion, grenadeImpactDeath, splashRotation, receiveDamageFromParent ) +{ + Assert( IsDefined( partIndex ) ); + Assert( IsDefined( stateIndex ) ); + Assert( IsDefined( level.destructible_type ) ); + Assert( level.destructible_type.size > 0 ); + + if ( IsDefined( modelName ) ) + modelName = ToLower( modelName ); + + destructibleIndex = ( level.destructible_type.size - 1 ); + + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ] = SpawnStruct(); + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "modelName" ] = modelName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "tagName" ] = tagName; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "health" ] = health; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validAttackers" ] = validAttackers; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validDamageZone" ] = validDamageZone; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "validDamageCause" ] = validDamageCause; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "alsoDamageParent" ] = alsoDamageParent; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "physicsOnExplosion" ] = physicsOnExplosion; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "grenadeImpactDeath" ] = grenadeImpactDeath; + // sanity check please. I set this here so that I don't have to do isdefined on every part evertime it gets hit + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "godModeAllowed" ] = false; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "splashRotation" ] = splashRotation; + level.destructible_type[ destructibleIndex ].parts[ partIndex ][ stateIndex ].v[ "receiveDamageFromParent" ] = receiveDamageFromParent; +} + +precache_destructibles() +{ + // I needed this to be seperate for vehicle scripts. + //--------------------------------------------------------------------- + // Precache referenced models and load referenced effects + //--------------------------------------------------------------------- + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts ) ) + return; + + //if ( !isdefined( level.precachedModels ) ) + // level.precachedModels = []; + + if ( IsDefined( level.destructible_type[ self.destructibleInfo].attachedModels ) ) + { + foreach ( attachedModel in level.destructible_type[ self.destructibleInfo].attachedModels ) + PreCacheModel( attachedModel.model ); + } + + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + for ( j = 0; j < level.destructible_type[ self.destructibleInfo].parts[ i ].size; j++ ) + { + if ( level.destructible_type[ self.destructibleInfo].parts[ i ].size <= j ) + continue; + + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "modelName" ] ) ) + { + PreCacheModel( level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "modelName" ] ); + } + + // in MP we have to precache animations that will be used + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "animation" ] ) ) + { + animGroups = level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "animation" ]; + foreach ( group in animGroups ) + { + if ( IsDefined( group[ "mpAnim" ] ) ) + noself_func( "precacheMpAnim", group[ "mpAnim" ] ); + } + } + + // find random attachements such as random advertisements on taxi cabs and precache them now + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "random_dynamic_attachment_1" ] ) ) + { + foreach ( model in level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "random_dynamic_attachment_1" ] ) + { + if ( IsDefined( model ) && model != "" ) + { + PreCacheModel( model ); + PreCacheModel( model + DESTROYED_ATTACHMENT_SUFFIX ); + } + } + foreach ( model in level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "random_dynamic_attachment_2" ] ) + { + if ( IsDefined( model ) && model != "" ) + { + PreCacheModel( model ); + PreCacheModel( model + DESTROYED_ATTACHMENT_SUFFIX ); + } + } + } + } + } +} + +add_destructible_fx() +{ + // I needed this to be seperate for vehicle scripts. + //--------------------------------------------------------------------- + // Precache referenced models and load referenced effects + //--------------------------------------------------------------------- + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts ) ) + return; + + //if ( !isdefined( level.precachedFX ) ) + // level.precachedFX = []; + + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + for ( j = 0; j < level.destructible_type[ self.destructibleInfo].parts[ i ].size; j++ ) + { + if ( level.destructible_type[ self.destructibleInfo].parts[ i ].size <= j ) + continue; + + part = level.destructible_type[ self.destructibleInfo].parts[ i ][ j ]; + + if ( IsDefined( part.v[ "fx_filename" ] ) ) + { + for ( g = 0; g < part.v[ "fx_filename" ].size; g++ ) + { + // for multiple checks on fx when doing conditional fx playing + fx_filenames = part.v[ "fx_filename" ][ g ]; + fx_tags = part.v[ "fx_tag" ][ g ]; + + if ( IsDefined( fx_filenames ) ) + { + assert( IsDefined( fx_tags ) ); + + // has we already set this up? + if ( IsDefined( part.v[ "fx" ] ) && IsDefined( part.v[ "fx" ][ g ] ) && part.v[ "fx" ][ g ].size == fx_filenames.size ) + continue; + + for ( idx = 0; idx < fx_filenames.size; idx++ ) + { + fx_filename = fx_filenames[idx]; + fx_tag = fx_tags[idx]; + + level.destructible_type[ self.destructibleInfo ].parts[ i ][ j ].v[ "fx" ][ g ][ idx ] = LoadFX( fx_filename, fx_tag ); + + //if ( !isdefined( level.precachedFX[ fx_filename ] ) ) + //{ + // level.precachedFX[ fx_filename ] = true; + // println( "loadfx( " + fx_filename + " )" ); + //} + } + } + } + } + + loopfx_filenames = level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "loopfx_filename" ]; + loopfx_tags = level.destructible_type[ self.destructibleInfo].parts[ i ][ j ].v[ "loopfx_tag" ]; + + if ( IsDefined( loopfx_filenames ) ) + { + assert( IsDefined( loopfx_tags ) ); + + // has we already set this up? + if ( IsDefined( part.v[ "loopfx" ] ) && part.v[ "loopfx" ].size == loopfx_filenames.size ) + continue; + + for ( idx = 0; idx < loopfx_filenames.size; idx++ ) + { + loopfx_filename = loopfx_filenames[idx]; + loopfx_tag = loopfx_tags[idx]; + + level.destructible_type[ self.destructibleInfo ].parts[ i ][ j ].v[ "loopfx" ][ idx ] = LoadFX( loopfx_filename, loopfx_tag ); + + //if ( !isdefined( level.precachedFX[ loopfx_filename ] ) ) + //{ + // level.precachedFX[ loopfx_filename ] = true; + // println( "loadfx( " + loopfx_filename + " )" ); + //} + } + } + } + } + +} + + +canDamageDestructible( testDestructible ) +{ + foreach ( destructible in self.destructibles ) + { + if ( destructible == testDestructible ) + return true; + } + return false; +} + +destructible_think() +{ + //--------------------------------------------------------------------- + // Force it to run update part one time first so we can have parts with + // 0 health that will start on level load instead of waiting for damage + //--------------------------------------------------------------------- + damage = 0; + modelName = self.model; + tagName = undefined; + point = self.origin; + direction_vec = undefined; + attacker = undefined; + damageType = undefined; + baseModelName = self.model; + self destructible_update_part( damage, modelName, tagName, point, direction_vec, attacker, damageType ); + + //--------------------------------------------------------------------- + // Wait until this entity takes damage + //--------------------------------------------------------------------- + self endon( "stop_taking_damage" ); + for ( ;; ) + { + // set these to undefined to clear them for each loop to save variables + damage = undefined; + attacker = undefined; + direction_vec = undefined; + point = undefined; + type = undefined; + modelName = undefined; + tagName = undefined; + partName = undefined; + dflags = undefined; + + self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags ); +// prof_begin( "_destructible" ); + + if ( !isdefined( damage ) ) + continue; + if ( IsDefined( attacker ) && IsDefined( attacker.type ) && attacker.type == "soft_landing" && !attacker canDamageDestructible( self ) ) + continue; + + if ( isSP() ) + damage *= SP_DAMAGE_BIAS; + else + damage *= MP_DAMAGE_BIAS; + + if ( damage <= 0 ) + continue; + + if ( isSP() ) + { + if ( IsDefined( attacker ) && IsPlayer( attacker ) ) + self.damageOwner = attacker; + } + else + { + if ( IsDefined( attacker ) && IsPlayer( attacker ) ) + self.damageOwner = attacker; + // osprey gunner could shoot a destructible, or anything with a player as the gunner + else if( IsDefined( attacker ) && IsDefined( attacker.gunner ) && IsPlayer( attacker.gunner ) ) + self.damageOwner = attacker.gunner; + } + + type = getDamageType( type ); + Assert( IsDefined( type ) ); + + // shotguns only do one notify so we need to amp up the damage + if ( is_shotgun_damage( attacker, type ) ) + { + if ( isSP() ) + damage *= SP_SHOTGUN_BIAS; + else + damage *= MP_SHOTGUN_BIAS; + } + + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + { + Print3d( point, ".", ( 1, 1, 1 ), 1.0, 0.5, 100 ); + if ( IsDefined( damage ) ) + IPrintLn( "damage amount: " + damage ); + if ( IsDefined( modelName ) ) + IPrintLn( "hit model: " + modelName ); + if ( IsDefined( tagName ) ) + IPrintLn( "hit model tag: " + tagName ); + else + IPrintLn( "hit model tag: " ); + } + #/ + + // override for when base model is damaged. We dont want to pass in empty strings + if ( !isdefined( modelName ) || ( modelName == "" ) ) + { + Assert( IsDefined( self.model ) ); + modelName = self.model; + } + if ( IsDefined( tagName ) && tagName == "" ) + { + if ( IsDefined( partName ) && partName != "" && partName != "tag_body" && partName != "body_animate_jnt" ) + tagName = partName; + else + tagName = undefined; + + baseModelTag = level.destructible_type[ self.destructibleInfo].parts[ 0 ][ 0 ].v[ "tagName" ]; + if ( IsDefined( baseModelTag ) && IsDefined( partName ) && ( baseModelTag == partName ) ) + tagName = undefined; + } + +// prof_end( "_destructible" ); + + // special handling for splash and projectile damage + if ( type == "splash" || type == "energy" ) + { + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + IPrintLn( "type = splash" ); + #/ + + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ 0 ][ 0 ].v[ "splash_damage_scaler" ] ) ) + damage *= level.destructible_type[ self.destructibleInfo].parts[ 0 ][ 0 ].v[ "splash_damage_scaler" ]; + else + { + if ( isSP() ) + damage *= SP_EXPLOSIVE_DAMAGE_BIAS; + else + damage *= MP_EXPLOSIVE_DAMAGE_BIAS; + } + + if ( (baseModelName == self.model) && IsDefined( self.script_dest_cover_dmg_model ) ) + self SetModel( self.script_dest_cover_dmg_model ); // the destructible splash damage expects the model to have the broken bits + + self destructible_splash_damage( Int( damage ), point, direction_vec, attacker, type ); + continue; + } + + self thread destructible_update_part( Int( damage ), modelName, tagName, point, direction_vec, attacker, type ); + } +} + +is_shotgun_damage( attacker, type ) +{ + if ( type != "bullet" ) + return false; + + if ( !isdefined( attacker ) ) + return false; + + currentWeapon = undefined; + if ( IsPlayer( attacker ) ) + { + currentweapon = attacker getCurrentWeapon(); + } + else if( isdefined( level.enable_ai_shotgun_destructible_damage ) && level.enable_ai_shotgun_destructible_damage ) + { + if( isdefined( attacker.weapon ) ) + currentweapon = attacker.weapon; + } + + if ( !isdefined( currentweapon ) ) + return false; + + class = weaponClass( currentweapon ); + if ( isdefined( class ) && class == "spread" ) + return true; + + return false; +} + +getPartAndStateIndex( modelName, tagName ) +{ + Assert( IsDefined( modelName ) ); + + info = SpawnStruct(); + info.v = []; + + partIndex = -1; + stateIndex = -1; + Assert( IsDefined( self.model ) ); + if ( ( ToLower( modelName ) == ToLower( self.model ) ) && ( !isdefined( tagName ) ) ) + { + modelName = self.model; + tagName = undefined; + partIndex = 0; + stateIndex = 0; + } + + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + stateIndex = self.destructible_parts[ i ].v[ "currentState" ]; + + if ( level.destructible_type[ self.destructibleInfo].parts[ i ].size <= stateIndex ) + continue; + + if ( !isdefined( tagName ) ) + continue; + + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "tagName" ] ) ) + { + partTagName = level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "tagName" ]; + if ( tolower( partTagName ) == tolower( tagName ) ) + { + partIndex = i; + break; + } + } + } + Assert( stateIndex >= 0 ); + Assert( IsDefined( partIndex ) ); + + info.v[ "stateIndex" ] = stateindex; + info.v[ "partIndex" ] = partindex; + + return info; +} + +destructible_update_part( damage, modelName, tagName, point, direction_vec, attacker, damageType, partInfo ) +{ + //--------------------------------------------------------------------- + // Find what part this is, or is a child of. If the base model was + // the entity that was damaged the part index will be -1 + //--------------------------------------------------------------------- + if ( !isdefined( self.destructible_parts ) ) + return; + if ( self.destructible_parts.size == 0 ) + return; + + + if( level.fast_destructible_explode ) + self endon ( "destroyed" ); + +// prof_begin( "_destructible" ); + + info = getPartAndStateIndex( modelName, tagName ); + stateIndex = info.v[ "stateIndex" ]; + partIndex = info.v[ "partIndex" ]; + +// prof_end( "_destructible" ); + + if ( partIndex < 0 ) + return; + + //--------------------------------------------------------------------- + // Deduct the damage amount from the part's health + // If the part runs out of health go to the next state + //--------------------------------------------------------------------- + state_before = stateIndex; + updateHealthValue = false; + delayModelSwap = false; +// prof_begin( "_destructible" ); + for ( ;; ) + { + stateIndex = self.destructible_parts[ partIndex ].v[ "currentState" ]; + + // We've finished with the last state already + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ] ) ) + break; + + // See if the model is also supposed to damage the parent + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ 0 ].v[ "alsoDamageParent" ] ) ) + { + if ( getDamageType( damageType ) != "splash" ) + { + ratio = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ 0 ].v[ "alsoDamageParent" ]; + parentDamage = Int( damage * ratio ); + self thread notifyDamageAfterFrame( parentDamage, attacker, direction_vec, point, damageType, "", "" ); + } + } + + // Loop through all parts to see which ones also need to get this damage applied to them ( based on their "receiveDamageFromParent" value ) + if ( partIndex == 0 && getDamageType( damageType ) != "splash") + { + for (i = 0; i < level.destructible_type[self.destructibleInfo].parts.size; i++) + { + part = level.destructible_type[self.destructibleInfo].parts[i]; + + if ( !isdefined( part[ 0 ].v[ "receiveDamageFromParent" ] ) ) + continue; + + partStateIndex = 0; + if (IsDefined(self.destructible_parts[ i ].v[ "currentState" ])) + partStateIndex = self.destructible_parts[ i ].v[ "currentState" ]; + + if (!IsDefined(part[partStateIndex])) + continue; + + if (!isdefined(part[ partStateIndex ].v[ "tagName" ])) + continue; + + childTagName = part[ partStateIndex ].v[ "tagName" ]; + + ratio = part[ 0 ].v[ "receiveDamageFromParent" ]; + Assert( ratio > 0 ); + + childDamage = Int( damage * ratio ); + self thread notifyDamageAfterFrame( childDamage, attacker, direction_vec, point, damageType, "", childTagName ); + } + } + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "health" ] ) ) + break; + if ( !isdefined( self.destructible_parts[ partIndex ].v[ "health" ] ) ) + break; + + // If we entered a new state, reset the part's health to the starting value for that state + if ( updateHealthValue ) + self.destructible_parts[ partIndex ].v[ "health" ] = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "health" ]; + updateHealthValue = false; + + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + { + IPrintLn( "stateindex: " + stateIndex ); + IPrintLn( "damage: " + damage ); + IPrintLn( "health( before ): " + self.destructible_parts[ partIndex ].v[ "health" ] ); + } + #/ + + // Handle grenades hitting glass parts. Grenades should make the glass completely break instead of just doing 1 damage and shattering the glass + if ( ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "grenadeImpactDeath" ] ) ) && ( damageType == "impact" ) ) + damage = 100000000; + + // Damage done is below the damage threshold for this part + if ( IsDefined( level.destructible_type[ self.destructibleInfo ].parts[ partIndex ][ stateIndex ].v[ "damage_threshold" ] ) && + level.destructible_type[ self.destructibleInfo ].parts[ partIndex ][ stateIndex ].v[ "damage_threshold" ] > damage ) + damage = 0; + + // apply the damage to the part if the attacker was a valid attacker + savedHealth = self.destructible_parts[ partIndex ].v[ "health" ]; + validAttacker = self isAttackerValid( partIndex, stateIndex, attacker ); + if ( validAttacker ) + { + validDamageCause = self isValidDamageCause( partIndex, stateIndex, damageType ); + if ( validDamageCause ) + { + if ( IsDefined( attacker ) ) + { + if ( IsPlayer( attacker ) ) + { + self.player_damage += damage; + } + else + { + if ( attacker != self ) + self.non_player_damage += damage; + } + } + + // Chad - ask Brent why we think melee is worth 100000 damage + if ( IsDefined( damageType ) ) + { + if ( damageType == "melee" || damageType == "impact" ) + damage = 100000; + } + + self.destructible_parts[ partIndex ].v[ "health" ] -= damage; + } + } + + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + IPrintLn( "health( after ): " + self.destructible_parts[ partIndex ].v[ "health" ] ); + #/ + + // if the part still has health left then we're done + if ( self.destructible_parts[ partIndex ].v[ "health" ] > 0 ) + { +// prof_end( "_destructible" ); + return; + } + + // cap on the number of destructibles killed in a single frame + if ( IsDefined( partInfo ) ) + { + partInfo.v[ "fxcost" ] = get_part_FX_cost_for_action_state( partIndex, self.destructible_parts[ partIndex ].v[ "currentState" ] ); + + add_destructible_to_frame_queue( self, partInfo, damage ); + + if ( !IsDefined( self.waiting_for_queue ) ) + self.waiting_for_queue = 1; + else + self.waiting_for_queue++; + + self waittill( "queue_processed", success ); + + self.waiting_for_queue--; + if ( self.waiting_for_queue == 0 ) + self.waiting_for_queue = undefined; + + if ( !success )// can we be destroyed this frame? + { + self.destructible_parts[ partIndex ].v[ "health" ] = savedHealth; + return; + } + } + + // if the part ran out of health then carry over to the next part + damage = Int( abs( self.destructible_parts[ partIndex ].v[ "health" ] ) ); + + // Brent asks - why is this condition here? It'll never trigger given that abs() does the following: + // "fabs returns the absolute value of x. Absolute value is a number's distance from zero on the number line. The absolute value of -4 is 4; the absolute value of 4 is 4." + // It should probably be removed + if ( damage < 0 ) + { + AssertEx( 0, "Logically, we should never get here, and I plan to remove this part of the script. Tell Boon at IW if you see this." ); +// prof_end( "_destructible" ); + return; + } + + self.destructible_parts[ partIndex ].v[ "currentState" ]++; + stateIndex = self.destructible_parts[ partIndex ].v[ "currentState" ]; + actionStateIndex = ( stateIndex - 1 ); + + // use these rather than re-getting them all the time. This insures that we do + // not overwrite their values too. + action_v = undefined; + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ] ) ) + action_v = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v; + + state_v = undefined; + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ] ) ) + state_v = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v; + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ] ) ) + { +// prof_end( "_destructible" ); + return; + } + + //--------------------------------------------------------------------- + // A state change is required so detach the old model or replace it if + // it's the base model that took the damage. + // Then attach the model ( if specified ) used for the new state + // Only do this if there is another state to go to, some parts might have + // fx or anims, or sounds but no next model to go to + //--------------------------------------------------------------------- + + // if the part is meant to explode on this state set a flag. Actual explosion will be done down below + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode" ] ) ) + self.exploding = true; + + // stop all previously looped sounds + if ( IsDefined( self.loopingSoundStopNotifies ) && IsDefined( self.loopingSoundStopNotifies[ toString( partIndex ) ] ) ) + { + for ( i = 0; i < self.loopingSoundStopNotifies[ toString( partIndex ) ].size; i++ ) + { + self notify( self.loopingSoundStopNotifies[ toString( partIndex ) ][ i ] ); + if ( isSP() && self.modeldummyon ) + self.modeldummy notify( self.loopingSoundStopNotifies[ toString( partIndex ) ][ i ] ); + } + self.loopingSoundStopNotifies[ toString( partIndex ) ] = undefined; + } + + // setup our destructible light if we want one and can find one + if ( IsDefined( action_v[ "break_nearby_lights" ] ) ) + { + self destructible_get_my_breakable_light( action_v[ "break_nearby_lights" ] ); + } + + // swap the model + // this doesn't work when threaded off to another function + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ] ) ) + { + if ( partIndex == 0 ) // base model damaged + { + newModel = state_v[ "modelName" ]; + if ( IsDefined( newModel ) && newModel != self.model ) + { + self SetModel( newModel ); + if ( isSP() && self.modeldummyon ) + self.modeldummy SetModel( newModel ); + destructible_splash_rotatation( state_v ); + } + } + else // Part was damaged, not the base model + { + // Handle a part getting damaged here + self hideapart( tagName ); + if ( isSP() && self.modeldummyon ) + self.modeldummy hideapart( tagName ); + + tagName = state_v[ "tagName" ]; + if ( IsDefined( tagName ) ) + { + self showapart( tagName ); + if ( isSP() && self.modeldummyon ) + self.modeldummy showapart( tagName ); + } + } + } + + eModel = get_dummy(); + + // If its exploding clear all previous animations on the destructible. The only animation that will play after this is an explosion animation + if ( IsDefined( self.exploding ) ) + self clear_anims( eModel ); + + // if the part has an anim then play it now + groupNumber = destructible_animation_think( action_v, eModel, damageType, partIndex ); + + // if the part has fx then play it now + groupNumber = destructible_fx_think( action_v, eModel, damageType, partIndex, groupNumber ); + + // send a message to force kill fx that have state_change_kill set + self notify( "FX_State_Change_Kill" + partIndex ); + + // if the part has a soundalias then play it now + groupNumber = destructible_sound_think( action_v, eModel, damageType, groupNumber ); + + // if the part has a looping fx then play it now + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx" ] ) ) + { + loopfx_size = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_filename" ].size; + + if ( loopfx_size > 0 ) + self notify( "FX_State_Change" + partIndex ); + + for ( idx = 0; idx < loopfx_size; idx++ ) + { + Assert( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_tag" ][ idx ] ) ); + loopfx = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx" ][ idx ]; + loopfx_tag = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_tag" ][ idx ]; + loopRate = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopfx_rate" ][ idx ]; + self thread loopfx_onTag( loopfx, loopfx_tag, loopRate, partIndex ); + } + } + + // if the part has a looping soundalias then start looping it now + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ] ) ) + { + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ].size; i++ ) + { + validSoundCause = self isValidSoundCause( "loopsoundCause", action_v, i, damageType ); + if ( validSoundCause ) + { + loopsoundAlias = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "loopsound" ][ i ]; + loopsoundTagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "tagName" ]; + self thread play_loop_sound_on_destructible( loopsoundAlias, loopsoundTagName ); + + if ( !isdefined( self.loopingSoundStopNotifies ) ) + self.loopingSoundStopNotifies = []; + if ( !isdefined( self.loopingSoundStopNotifies[ toString( partIndex ) ] ) ) + self.loopingSoundStopNotifies[ toString( partIndex ) ] = []; + size = self.loopingSoundStopNotifies[ toString( partIndex ) ].size; + self.loopingSoundStopNotifies[ toString( partIndex ) ][ size ] = "stop sound" + loopsoundAlias; + } + } + } + + // if the part is supposed to trigger a car alarm + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "triggerCarAlarm" ] ) ) + { + self thread do_car_alarm(); + } + + // if the part is supposed to trigger a car alarm + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "break_nearby_lights" ] ) ) + { + self thread break_nearest_light(); + } + + // if the part should drain health then start the drain + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_amount" ] ) ) + { + self notify( "Health_Drain_State_Change" + partIndex ); + healthdrain_amount = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_amount" ]; + healthdrain_interval = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "healthdrain_interval" ]; + healthdrain_modelName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "modelName" ]; + healthdrain_tagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "tagName" ]; + badplaceRadius = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "badplace_radius" ]; + badplaceTeam = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "badplace_team" ]; + if ( healthdrain_amount > 0 ) + { + Assert( ( IsDefined( healthdrain_interval ) ) && ( healthdrain_interval > 0 ) ); + self thread health_drain( healthdrain_amount, healthdrain_interval, partIndex, healthdrain_modelName, healthdrain_tagName, badplaceRadius, badplaceTeam ); + } + } + + // DOT + // - Usually this will happen the same time as explode. + // - This is setup so it can theoretically happen at any given state + + dots = level.destructible_type[ self.destructibleInfo ].parts[ partIndex ][ actionStateIndex ].v[ "dot" ]; + + if ( IsDefined( dots ) ) + { + foreach ( dot in dots ) + { + dotIndex = dot.index; + + if ( dot.type == "predefined" && IsDefined( dotIndex ) ) + { + dot_group = []; + + foreach ( triggerIndex in level.destructible_type[ self.destructibleInfo ].destructible_dots[ dotIndex ] ) + { + classname = triggerIndex[ "classname" ]; + _dot = undefined; + + switch ( classname ) + { + case "trigger_radius": + origin = triggerIndex[ "origin" ]; + spawnflags = triggerIndex[ "spawnflags" ]; + radius = triggerIndex[ "radius" ]; + height = triggerindex[ "height" ]; + + _dot = createDOT_radius( self.origin + origin, spawnflags, radius, height ); + _dot.ticks = dot.ticks; + dot_group[ dot_group.size ] = _dot; + break; + default: + } + } + level thread startDOT_group( dot_group ); + } + else + { + if ( IsDefined( dot ) ) + { + if ( IsDefined( dot.tag ) ) + dot setDOT_origin( self GetTagOrigin( dot.tag ) ); + level thread startDOT_group( [ dot ] ); + } + } + } + dots = undefined; + } + + // if the part is meant to explode on this state then do it now. Causes all attached models to become physics with the specified force + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode" ] ) ) + { + delayModelSwap = true; + force_min = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_force_min" ]; + force_max = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_force_max" ]; + angularImpulse_min = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_angularImpulse_min" ]; + angularImpulse_max = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_angularImpulse_max" ]; + range = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_range" ]; + mindamage = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_mindamage" ]; + maxdamage = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "explode_maxdamage" ]; + continueDamage = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "continueDamage" ]; + originOffset = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "originOffset" ]; + earthQuakeScale = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "earthQuakeScale" ]; + earthQuakeRadius = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "earthQuakeRadius" ]; + originOffset3d = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "originOffset3d" ]; + delaytime = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "delaytime" ]; + + if ( IsDefined( attacker ) && attacker != self ) + { + // Achievement Hook + self.attacker = attacker; + + // Only add .damage_type to script_vehicles that happen to be destructibles (ie UAZ) + // This hook provides info so the vehicle can do _player_stat::register_kill() + if ( self.code_classname == "script_vehicle" ) + { + self.damage_type = damageType; + } + } + + self thread explode( partIndex, force_min, force_max, range, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, attacker, originOffset3d, delaytime, angularImpulse_min, angularImpulse_max ); + } + + // if the part should do physics here then initiate the physics and velocity + physTagOrigin = undefined; + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "physics" ] ) ) + { + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "physics" ].size; i++ ) + { + physTagOrigin = undefined; + physTagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "physics_tagName" ][ i ]; + physVelocity = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "physics_velocity" ][ i ]; + + initial_velocity = undefined; + if ( IsDefined( physVelocity ) ) + { + physTagAngles = undefined; + if ( IsDefined( physTagName ) ) + physTagAngles = self GetTagAngles( physTagName ); + else if ( IsDefined( tagName ) ) + physTagAngles = self GetTagAngles( tagName ); + Assert( IsDefined( physTagAngles ) ); + + physTagOrigin = undefined; + if ( IsDefined( physTagName ) ) + physTagOrigin = self GetTagOrigin( physTagName ); + else if ( IsDefined( tagName ) ) + physTagOrigin = self GetTagOrigin( tagName ); + Assert( IsDefined( physTagOrigin ) ); + + phys_x = physVelocity[ 0 ] - 5 + RandomFloat( 10 ); + phys_y = physVelocity[ 1 ] - 5 + RandomFloat( 10 ); + phys_z = physVelocity[ 2 ] - 5 + RandomFloat( 10 ); + + forward = AnglesToForward( physTagAngles ) * phys_x * RandomFloatRange( 80, 110 ); + right = AnglesToRight( physTagAngles ) * phys_y * RandomFloatRange( 80, 110 ); + up = AnglesToUp( physTagAngles ) * phys_z * RandomFloatRange( 80, 110 ); + + initial_velocity = forward + right + up; + + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + { + thread draw_line_for_time( physTagOrigin, physTagOrigin + initial_velocity, 1, 1, 1, 5.0 ); + } + #/ + } + else + { + initial_velocity = point; + impactDir = ( 0, 0, 0 ); + if ( IsDefined( attacker ) ) + { + impactDir = attacker.origin; + initial_velocity = VectorNormalize( point - impactDir ); + initial_velocity *= 200; + } + } + Assert( IsDefined( initial_velocity ) ); + + if ( IsDefined( physTagName ) ) + { + // Do physics on another part, and continue this thread since the current part is still unaffected by the physics + + // get the partIndex that cooresponds to what the tagname is + physPartIndex = undefined; + for ( j = 0; j < level.destructible_type[ self.destructibleInfo].parts.size; j++ ) + { + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts[ j ][ 0 ].v[ "tagName" ] ) ) + continue; + + if ( level.destructible_type[ self.destructibleInfo].parts[ j ][ 0 ].v[ "tagName" ] != physTagName ) + continue; + + physPartIndex = j; + break; + } + + if ( IsDefined( physTagOrigin ) ) + self thread physics_launch( physPartIndex, 0, physTagOrigin, initial_velocity ); + else + self thread physics_launch( physPartIndex, 0, point, initial_velocity ); + } + else + { + // Do physics on this part, therefore ending this thread + + if ( IsDefined( physTagOrigin ) ) + self thread physics_launch( partIndex, actionStateIndex, physTagOrigin, initial_velocity ); + else + self thread physics_launch( partIndex, actionStateIndex, point, initial_velocity ); + +// prof_end( "_destructible" ); + return; + } + } + } + + // if the part should notify, for a function on a previous state + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v ) && + IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "functionNotify" ] ) ) + { + self notify( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "functionNotify" ] ); + } + + // if the part should call a function on its entity + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "function" ] ) ) + { + self thread [[ level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v[ "function" ] ]](); + } + + + updateHealthValue = true; + } +// prof_end( "_destructible" ); +} + +destructible_splash_rotatation( v ) +{ + // rotate model due to splash damage direction, optional + model_rotation = v[ "splashRotation" ]; + model_rotate_to = v[ "rotateTo" ]; + + if ( !isdefined( model_rotate_to ) ) + return; + if ( !isdefined( model_rotation ) ) + return; + if ( !model_rotation ) + return; + self.angles = ( self.angles[ 0 ], model_rotate_to[ 1 ], self.angles[ 2 ] ); +} + +// parameter damageType can be single damage or multiple damages separated by spaces +damage_not( damageType ) +{ + toks = StrTok( damageType, " " ); + damages_tok = StrTok( "splash melee bullet splash impact unknown", " " ); + new_string = ""; + + foreach ( idx, tok in toks ) + damages_tok = array_remove( damages_tok, tok ); + + foreach ( damages in damages_tok ) + new_string += damages + " "; + + return new_string; +} + +destructible_splash_damage( damage, point, direction_vec, attacker, damageType ) +{ + if ( damage <= 0 ) + return; + + if ( IsDefined( self.exploded ) ) + return; + + //------------------------------------------------------------------------ + // Fill an array of all possible parts that might have been splash damaged + //------------------------------------------------------------------------ + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts ) ) + return; + + damagedParts = self getAllActiveParts( direction_vec ); + + if ( damagedParts.size <= 0 ) + return; + + damagedParts = self setDistanceOnParts( damagedParts, point ); + + closestPartDist = getLowestPartDistance( damagedParts ); + Assert( IsDefined( closestPartDist ) ); + + //-------------------------------------------------------------------------- + // Damage each part depending on how close it was to the splash damage point + //-------------------------------------------------------------------------- + +// prof_begin( "_destructible" ); + + foreach ( part in damagedParts ) + { + distanceMod = ( part.v[ "distance" ] * 1.4 ); + damageAmount = ( damage - ( distanceMod - closestPartDist ) ); + + if ( damageAmount <= 0 ) + continue; + + if ( IsDefined( self.exploded ) ) + continue; + + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + { + if ( IsDefined( part.v[ "tagName" ] ) ) + Print3d( self GetTagOrigin( part.v[ "tagName" ] ), damageAmount, ( 1, 1, 1 ), 1.0, 0.5, 200 ); + } + #/ + + self thread destructible_update_part( damageAmount, part.v[ "modelName" ], part.v[ "tagName" ], point, direction_vec, attacker, damageType, part ); + } + +// prof_end( "_destructible" ); +} + +// Called on a destructible entity, returns a list of all tags and models that are currently active. +// - AFAIK models aren't attached to destructibles any more so it's probably just a list of tags +// - Uses direction_vec to set an angle in the level's info for this destructible type, for later use by the destructible_splash_rotatation() function. +getAllActiveParts( direction_vec ) +{ + activeParts = []; + + Assert( IsDefined( self.destructibleInfo) ); + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts ) ) + return activeParts; + +// prof_begin( "_destructible" ); + + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + partIndex = i; + currentState = self.destructible_parts[ partIndex ].v[ "currentState" ]; + + // Splash damage rotation, rotation angle only calculated for state that has this option enabled + for ( j = 0; j < level.destructible_type[ self.destructibleInfo].parts[ partIndex ].size; j++ ) + { + splash_rotation = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ j ].v[ "splashRotation" ]; + if ( IsDefined( splash_rotation ) && splash_rotation ) + { + rotate_to_angle = VectorToAngles( direction_vec ); + rotate_to_angle_y = rotate_to_angle[ 1 ] - 90; + level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ j ].v[ "rotateTo" ] = ( 0, rotate_to_angle_y, 0 ); + } + } + + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ currentState ] ) ) + continue; + + tagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ currentState ].v[ "tagName" ]; + if ( !isdefined( tagName ) ) + tagName = ""; + + if ( tagName == "" ) + continue; + + modelName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ currentState ].v[ "modelName" ]; + if ( !isdefined( modelName ) ) + modelName = ""; + + activePartIndex = activeParts.size; + activeParts[ activePartIndex ] = SpawnStruct(); + activeParts[ activePartIndex ].v[ "modelName" ] = modelName; + activeParts[ activePartIndex ].v[ "tagName" ] = tagName; + } + +// prof_end( "_destructible" ); + + return activeParts; +} + +setDistanceOnParts( partList, point ) +{ +// prof_begin( "_destructible" ); + + for ( i = 0; i < partList.size; i++ ) + { + d = Distance( point, self GetTagOrigin( partList[ i ].v[ "tagName" ] ) ); + partList[ i ].v[ "distance" ] = d; + } + +// prof_end( "_destructible" ); + + return partList; +} + +getLowestPartDistance( partList ) +{ + closestDist = undefined; + +// prof_begin( "_destructible" ); + + foreach ( part in partList ) + { + Assert( IsDefined( part.v[ "distance" ] ) ); + d = part.v[ "distance" ]; + + if ( !isdefined( closestDist ) ) + closestDist = d; + + if ( d < closestDist ) + closestDist = d; + } + +// prof_end( "_destructible" ); + + return closestDist; +} + + +isValidSoundCause( soundCauseVar, action_v, soundIndex, damageType, groupNum ) +{ + if ( isdefined( groupNum ) ) + soundCause = action_v[ soundCauseVar ][ groupNum ][ soundIndex ]; + else + soundCause = action_v[ soundCauseVar ][ soundIndex ]; + + if ( !isdefined( soundCause ) ) + return true; + + if ( soundCause == damageType ) + return true; + + return false; +} + +isAttackerValid( partIndex, stateIndex, attacker ) +{ + // return true if the vehicle is being force exploded + if ( IsDefined( self.forceExploding ) ) + return true; + + // return false if the vehicle is trying to explode but it's not allowed to + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "explode" ] ) ) + { + if ( IsDefined( self.dontAllowExplode ) ) + return false; + } + + if ( !isdefined( attacker ) ) + return true; + + if ( attacker == self ) + return true; + + sType = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "validAttackers" ]; + if ( !isdefined( sType ) ) + return true; + + if ( sType == "no_player" ) + { + if ( !isplayer( attacker ) ) + return true; + if ( !isdefined( attacker.damageIsFromPlayer ) ) + return true; + if ( attacker.damageIsFromPlayer == false ) + return true; + } + else + if ( sType == "player_only" ) + { + if ( IsPlayer( attacker ) ) + return true; + if ( IsDefined( attacker.damageIsFromPlayer ) && attacker.damageIsFromPlayer ) + return true; + } + else + if ( sType == "no_ai" && IsDefined( level.isAIfunc ) ) + { + if ( ![[ level.isAIfunc ]]( attacker ) ) + return true; + } + else + if ( sType == "ai_only" && IsDefined( level.isAIfunc ) ) + { + if ( [[ level.isAIfunc ]]( attacker ) ) + return true; + } + else + { + AssertMsg( "Invalid attacker rules on destructible vehicle. Valid types are: ai_only, no_ai, player_only, no_player" ); + } + + return false; +} + +isValidDamageCause( partIndex, stateIndex, damageType ) +{ + if ( !isdefined( damageType ) ) + return true; + + godModeAllowed = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "godModeAllowed" ]; + if ( godModeAllowed && ( ( IsDefined( self.godmode ) && self.godmode ) || ( IsDefined( self.script_bulletshield ) && self.script_bulletshield ) && damageType == "bullet" ) ) + return false; + + validType = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "validDamageCause" ]; + if ( !isdefined( validType ) ) + return true; + + if ( ( validType == "splash" ) && damageType != "splash" ) + return false; + + if ( ( validType == "no_splash" ) && damageType == "splash" ) + return false; + + if ( ( validType == "no_melee" ) && damageType == "melee" || damageType == "impact" ) + return false; + + if ( ( validType == "bullet" ) && damageType != "bullet" ) + return false; + + return true; +} + +getDamageType( type ) +{ + //returns a simple damage type: melee, bullet, splash, or unknown + + if ( !isdefined( type ) ) + return "unknown"; + + type = ToLower( type ); + switch( type ) + { + case "mod_melee": + case "mod_melee_alt": + case "mod_crush": + case "melee": + return "melee"; + case "mod_pistol_bullet": + case "mod_rifle_bullet": + case "bullet": + return "bullet"; + case "mod_grenade": + case "mod_grenade_splash": + case "mod_projectile": + case "mod_projectile_splash": + case "mod_explosive": + case "splash": + return "splash"; + case "mod_impact": + return "impact"; + case "mod_energy": + return "energy"; + case "unknown": + return "unknown"; + default: + return "unknown"; + } +} + +damage_mirror( parent, modelName, tagName ) +{ + self notify( "stop_damage_mirror" ); + self endon( "stop_damage_mirror" ); + parent endon( "stop_taking_damage" ); + + self SetCanDamage( true ); + for ( ;; ) + { + self waittill( "damage", damage, attacker, direction_vec, point, type ); + parent notify( "damage", damage, attacker, direction_vec, point, type, modelName, tagName ); + damage = undefined; + attacker = undefined; + direction_vec = undefined; + point = undefined; + type = undefined; + } +} + +add_damage_owner_recorder() +{ + // Mackey added to track who is damaging the car + self.player_damage = 0; + self.non_player_damage = 0; + + self.car_damage_owner_recorder = true; +} + +loopfx_onTag( loopfx, loopfx_tag, loopRate, partIndex ) +{ + self endon( "FX_State_Change" + partIndex ); + self endon( "delete_destructible" ); + level endon( "putout_fires" ); + + while( isdefined( self ) ) + { + eModel = get_dummy(); + PlayFXOnTag( loopfx, eModel, loopfx_tag ); + wait loopRate; + } +} + +health_drain( amount, interval, partIndex, modelName, tagName, badplaceRadius, badplaceTeam ) +{ + self endon( "Health_Drain_State_Change" + partIndex ); + level endon( "putout_fires" ); + self endon( "destroyed" ); + + if( IsDefined( badplaceRadius ) && IsDefined( level.destructible_badplace_radius_multiplier ) ) + { + badplaceRadius *= level.destructible_badplace_radius_multiplier; + } + + if( IsDefined( amount ) &&IsDefined( level.destructible_health_drain_amount_multiplier ) ) + { + amount *= level.destructible_health_drain_amount_multiplier; + } + + wait interval; + + self.healthDrain = true; + + uniqueName = undefined; + + // disable the badplace radius call if level.disable_destructible_bad_places is true + if ( IsDefined( level.disable_destructible_bad_places ) && level.disable_destructible_bad_places ) + badplaceRadius = undefined; + + if ( IsDefined( badplaceRadius ) && IsDefined( level.badplace_cylinder_func ) ) + { + uniqueName = "" + GetTime(); + if ( !isdefined( self.disableBadPlace ) ) + { + if ( IsDefined( self.script_radius ) ) + { + // overwrite the badplace radius from the map + badplaceRadius = self.script_radius; + } + if ( isSP() && IsDefined( badplaceTeam ) ) + { + if ( badplaceTeam == "both" ) + call [[ level.badplace_cylinder_func ]]( uniqueName, 0, self.origin, badplaceRadius, 128, "allies", "bad_guys" ); + else + call [[ level.badplace_cylinder_func ]]( uniqueName, 0, self.origin, badplaceRadius, 128, badplaceTeam ); + self thread badplace_remove( uniqueName ); + } + else + { + call [[ level.badplace_cylinder_func ]]( uniqueName, 0, self.origin, badplaceRadius, 128 ); + self thread badplace_remove( uniqueName ); + } + } + } + + while ( isdefined( self ) && self.destructible_parts[ partIndex ].v[ "health" ] > 0 ) + { + /# + if ( GetDvarInt( "debug_destructibles" , 0 ) == 1 ) + { + IPrintLn( "health before damage: " + self.destructible_parts[ partIndex ].v[ "health" ] ); + IPrintLn( "doing " + amount + " damage" ); + } + #/ + self notify( "damage", amount, self, ( 0, 0, 0 ), ( 0, 0, 0 ), "MOD_UNKNOWN", modelName, tagName ); + wait interval; + } + + self notify( "remove_badplace" ); +} + +badplace_remove( uniqueName ) +{ + self waittill_any( "destroyed", "remove_badplace" ); + + Assert( IsDefined( uniqueName ) ); + Assert( IsDefined( level.badplace_delete_func ) ); + call [[ level.badplace_delete_func ]]( uniqueName ); +} + +physics_launch( partIndex, stateIndex, point, initial_velocity ) +{ + physicsObject = self physics_object_create( partIndex, stateIndex ); + + // Do physics on the model + physicsObject PhysicsLaunchClient( point, initial_velocity ); +} + +physics_launch_with_impulse( partIndex, stateIndex, linearImpulse, angularImpulse ) +{ + physicsObject = self physics_object_create( partIndex, stateIndex ); + + // Do physics on the model + physicsObject PhysicsLaunchClientWithImpulse( linearImpulse, angularImpulse ); +} + +physics_object_create( partIndex, stateIndex ) +{ + modelName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "modelName" ]; + tagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ stateIndex ].v[ "tagName" ]; + + self hideapart( tagName ); + + /# + if ( GetDvarInt( "destructibles_enable_physics", 1 ) == 0 ) + return; + #/ + + // If we've reached the max number of spawned physics models for destructible vehicles then delete one before creating another + if ( level.destructibleSpawnedEnts.size >= level.destructibleSpawnedEntsLimit ) + physics_object_remove( level.destructibleSpawnedEnts[ 0 ] ); + + // Spawn a model to use for physics using the modelname and position of the part + physicsObject = Spawn( "script_model", self GetTagOrigin( tagName ) ); + physicsObject.angles = self GetTagAngles( tagName ); + physicsObject SetModel( modelName ); + + /# + // Give it a targetname that makes it easy to identify using g_entinfo + physicsObject.targetname = modelName + "(thrown physics model)"; + #/ + + // Keep track of the new part so it can be removed later if we reach the max + level.destructibleSpawnedEnts[ level.destructibleSpawnedEnts.size ] = physicsObject; + + return physicsObject; +} + +physics_object_remove( ent ) +{ + newArray = []; + for ( i = 0; i < level.destructibleSpawnedEnts.size; i++ ) + { + if ( level.destructibleSpawnedEnts[ i ] == ent ) + continue; + newArray[ newArray.size ] = level.destructibleSpawnedEnts[ i ]; + } + level.destructibleSpawnedEnts = newArray; + + if ( isdefined( ent ) ) + ent Delete(); +} + +explode( partIndex, force_min, force_max, range, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, attacker, originOffset3d, delaytime, angularImpulse_min, angularImpulse_max ) +{ + Assert( IsDefined( force_min ) ); + Assert( IsDefined( force_max ) ); + + if( IsDefined( range ) && IsDefined( level.destructible_explosion_radius_multiplier ) ) + { + range *= level.destructible_explosion_radius_multiplier; + } + + if ( !isdefined( originOffset ) ) + originOffset = 80; + if ( !isdefined( originOffset3d) ) + originOffset3d = (0,0,0); + + if ( !isdefined( continueDamage ) || ( IsDefined( continueDamage ) && !continueDamage ) ) + { + if ( IsDefined( self.exploded ) ) + return; + self.exploded = true; + } + + if (!isdefined( delaytime) ) + delaytime = 0; + + self notify( "exploded", attacker ); + level notify( "destructible_exploded", self, attacker ); + if ( self.code_classname == "script_vehicle" ) + self notify( "death", attacker, self.damage_type ); + + // check if there is a disconnect paths brush to disconnect any traverses + if ( isSP() ) + self thread disconnectTraverses(); + + + if( ! level.fast_destructible_explode ) + wait 0.05; + + // entity could be deleted during previous wait + if ( !IsDefined( self ) ) + return; + + currentState = self.destructible_parts[ partIndex ].v[ "currentState" ]; + Assert( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ] ) ); + tagName = undefined; + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ currentState ] ) ) + tagName = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ currentState ].v[ "tagName" ]; + + if ( IsDefined( tagName ) ) + explosionOrigin = self GetTagOrigin( tagName ); + else + explosionOrigin = self.origin; + + self notify( "damage", maxdamage, self, ( 0, 0, 0 ), explosionOrigin, "MOD_EXPLOSIVE", "", "" ); + + self notify( "stop_car_alarm" ); + + waittillframeend; + +// prof_begin( "_destructible" ); + + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts ) ) + { + for ( i = ( level.destructible_type[ self.destructibleInfo].parts.size - 1 ); i >= 0; i-- ) + { + if ( i == partIndex ) + continue; + + stateIndex = self.destructible_parts[ i ].v[ "currentState" ]; + if ( stateIndex >= level.destructible_type[ self.destructibleInfo].parts[ i ].size ) + stateIndex = level.destructible_type[ self.destructibleInfo].parts[ i ].size - 1; + modelName = level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "modelName" ]; + tagName = level.destructible_type[ self.destructibleInfo].parts[ i ][ stateIndex ].v[ "tagName" ]; + + if ( !isdefined( modelName ) ) + continue; + if ( !isdefined( tagName ) ) + continue; + + currentHealth = 0; + if (IsDefined(self.destructible_parts[i].v["health"])) + currentHealth = self.destructible_parts[i].v["health"]; + + defaultHealth = 0; + if (IsDefined( level.destructible_type[ self.destructibleInfo].parts[i][stateIndex].v["health"])) + defaultHealth = level.destructible_type[ self.destructibleInfo].parts[i][stateIndex].v["health"]; + + if (defaultHealth > 0 && currentHealth <= 0) + continue; + + // dont do physics on parts that are supposed to be removed on explosion + if ( IsDefined( level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "physicsOnExplosion" ] ) ) + { + if ( level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "physicsOnExplosion" ] > 0 ) + { + velocityScaler = level.destructible_type[ self.destructibleInfo].parts[ i ][ 0 ].v[ "physicsOnExplosion" ]; + + point = self GetTagOrigin( tagName ); + initial_velocity = VectorNormalize( point - explosionOrigin ); + initial_velocity *= RandomFloatRange( force_min, force_max ) * velocityScaler; + + if ( isdefined( angularImpulse_min ) && isdefined( angularImpulse_max ) ) + { + angularImpulse = randomvectorrange( angularImpulse_min, angularImpulse_max ); + + self thread physics_launch_with_impulse( i, stateIndex, initial_velocity, angularImpulse ); + } + else + { + + self thread physics_launch( i, stateIndex, point, initial_velocity ); + } + continue; + } + } + //self.destructible_parts[ i ] Hide(); + } + } + +// prof_end( "_destructible" ); + + stopTakingDamage = ( !isdefined( continueDamage ) || ( IsDefined( continueDamage ) && !continueDamage ) ); + if ( stopTakingDamage ) + self notify( "stop_taking_damage" ); + + if( ! level.fast_destructible_explode ) + wait 0.05; + + // entity could be deleted during previous wait + if ( !IsDefined( self ) ) + return; + + damageLocation = explosionOrigin + ( 0, 0, originOffset ) + originOffset3d; + + isVehicle = ( GetSubStr( level.destructible_type[ self.destructibleInfo].v[ "type" ], 0, 7 ) == "vehicle" ); + + if ( isVehicle ) + { + anim.lastCarExplosionTime = GetTime(); + anim.lastCarExplosionDamageLocation = damageLocation; + anim.lastCarExplosionLocation = explosionOrigin; + anim.lastCarExplosionRange = range; + } + + // turn off friendly fire when they blow up so the player doesn't get accidental friendly fire mission failure + level thread set_disable_friendlyfire_value_delayed( 1 ); + + + if ( delaytime > 0 ) + wait (delaytime); + + //using this in hamburg where player has no control and needs to be protected from cars blowing up nearby. It's not the players fault!! + if( isdefined( level.destructible_protection_func ) ) + thread [[level.destructible_protection_func]](); + + if ( isSP() ) + { + if ( level.gameskill == 0 && !self player_touching_post_clip() ) + self RadiusDamage( damageLocation, range, maxdamage, mindamage, self, "MOD_RIFLE_BULLET" ); + else + self RadiusDamage( damageLocation, range, maxdamage, mindamage, self ); + + if ( IsDefined( self.damageOwner ) && isVehicle ) + { + self.damageOwner notify( "destroyed_car" ); + level notify( "player_destroyed_car", self.damageOwner, damageLocation ); + } + } + else + { + weapon = "destructible_toy"; + if ( isVehicle ) + weapon = "destructible_car"; + + if ( !isdefined( self.damageOwner ) ) + { + self RadiusDamage( damageLocation, range, maxdamage, mindamage, self, "MOD_EXPLOSIVE", weapon ); + } + else + { + self RadiusDamage( damageLocation, range, maxdamage, mindamage, self.damageOwner, "MOD_EXPLOSIVE", weapon ); + if ( isVehicle ) + { + self.damageOwner notify( "destroyed_car" ); + level notify( "player_destroyed_car", self.damageOwner, damageLocation ); + } + } + } + + if ( IsDefined( earthQuakeScale ) && IsDefined( earthQuakeRadius ) ) + Earthquake( earthQuakeScale, 2.0, damageLocation, earthQuakeRadius ); + + /# + if ( GetDvarInt( "destructibles_show_radiusdamage" ) == 1 ) + thread debug_radiusdamage_circle( damageLocation, range, maxdamage, mindamage ); + #/ + + // explosion damage done, resume friendly fire if it was enabled + level thread set_disable_friendlyfire_value_delayed( 0, 0.05 ); + + magnitudeScaler = 0.01; + magnitude = range * magnitudeScaler; + Assert( magnitude > 0 ); + range *= .99; + PhysicsExplosionSphere( explosionOrigin, range, 0, magnitude ); + + if ( stopTakingDamage ) + { + self SetCanDamage( false ); + self thread cleanupVars(); + } + + self notify( "destroyed" ); +} + +cleanupVars() +{ + // wait so we can make sure they are no longer needed + wait 0.05; + + while ( isdefined( self ) && isdefined( self.waiting_for_queue ) ) + { + self waittill( "queue_processed" ); + wait 0.05; + } + + if ( !isdefined( self ) ) + return; + + self.animsapplied = undefined; + self.attacker = undefined; + self.car_damage_owner_recorder = undefined; + self.caralarm = undefined; + self.damageowner = undefined; + self.destructible_parts = undefined; + self.destructible_type = undefined; + self.destructibleInfo = undefined; + self.healthdrain = undefined; + self.non_player_damage = undefined; + self.player_damage = undefined; + + if ( !IsDefined( level.destructible_cleans_up_more ) ) + return; + + self.script_noflip = undefined; + self.exploding = undefined; + self.loopingsoundstopnotifies = undefined; + self.car_alarm_org = undefined; +} + +set_disable_friendlyfire_value_delayed( value, delay ) +{ + level notify( "set_disable_friendlyfire_value_delayed" ); + level endon( "set_disable_friendlyfire_value_delayed" ); + + Assert( IsDefined( value ) ); + + if ( IsDefined( delay ) ) + wait delay; + + level.friendlyFireDisabledForDestructible = value; +} + +/* +arcadeMode_car_kill() +{ + if ( !isSP() ) + return false; + + if ( !arcadeMode() ) + return false; + + if ( level.script == "ac130" ) + return false; + + if ( IsDefined( level.allCarsDamagedByPlayer ) ) + return true; + + return self maps\_gameskill::player_did_most_damage(); +} +*/ +connectTraverses() +{ + clip = get_traverse_disconnect_brush(); + + if ( !isdefined( clip ) ) + return; + + Assert( IsDefined( level.connectPathsFunction ) ); + clip call [[ level.connectPathsFunction ]](); + clip.origin -= ( 0, 0, 10000 ); +} + +disconnectTraverses() +{ + clip = get_traverse_disconnect_brush(); + + if ( !isdefined( clip ) ) + return; + + clip.origin += ( 0, 0, 10000 ); + Assert( IsDefined( level.disconnectPathsFunction ) ); + clip call [[ level.disconnectPathsFunction ]](); + clip.origin -= ( 0, 0, 10000 ); +} + +get_traverse_disconnect_brush() +{ + if ( !isdefined( self.target ) ) + return undefined; + + targets = GetEntArray( self.target, "targetname" ); + foreach ( target in targets ) + { + if ( IsSpawner( target ) ) + continue; + if ( IsDefined( target.script_destruct_collision ) ) + continue; + if ( target.code_classname == "light" ) + continue; + if ( !target.spawnflags & 1 ) + continue; + return target; + } +} + +hideapart( tagName ) +{ + self HidePart( tagName ); +} + +showapart( tagName ) +{ + self ShowPart( tagName ); +} + +disable_explosion() +{ + self.dontAllowExplode = true; +} + +force_explosion() +{ + self.dontAllowExplode = undefined; + self.forceExploding = true; + self notify( "damage", 100000, self, self.origin, self.origin, "MOD_EXPLOSIVE", "", "" ); +} + +get_dummy() +{ + if ( !isSP() ) + return self; + + if ( self.modeldummyon ) + eModel = self.modeldummy; + else + eModel = self; + return eModel; +} + +play_loop_sound_on_destructible( alias, tag ) +{ + eModel = get_dummy(); + + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + if ( IsDefined( tag ) ) + org.origin = eModel GetTagOrigin( tag ); + else + org.origin = eModel.origin; + + org PlayLoopSound( alias ); + + eModel thread force_stop_sound( alias ); + + eModel waittill( "stop sound" + alias ); + if ( !isdefined( org ) ) + return; + + org StopLoopSound( alias ); + org Delete(); +} + +force_stop_sound( alias ) +{ + self endon( "stop sound" + alias ); + + level waittill( "putout_fires" ); + self notify( "stop sound" + alias ); +} + +notifyDamageAfterFrame( damage, attacker, direction_vec, point, damageType, modelName, tagName ) +{ + // (Boon Oct2012: This function appears to be designed to transmit damage from child to/from parent parts. It can get + // called several times for one damage event, and level.notifyDamageAfterFrame is usually not defined so it then generates + // a cascade of damage notifies. Frankly, I can't understand how it works or how it is supposed to work, and I strongly + // suspect it's very fragile.) + + waittillframeend; + if ( IsDefined( self.exploded ) ) + { + return; + } + + if ( isSP() ) + damage /= SP_DAMAGE_BIAS; + else + damage /= MP_DAMAGE_BIAS; + + self notify( "damage", damage, attacker, direction_vec, point, damageType, modelName, tagName ); +} + +play_sound( alias, tag ) +{ + if ( IsDefined( tag ) ) + { + org = Spawn( "script_origin", self GetTagOrigin( tag ) ); + org Hide(); + org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) ); + } + else + { + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + org Hide(); + org.origin = self.origin; + org.angles = self.angles; + org LinkTo( self ); + } + + org PlaySound( alias ); + wait( 5.0 ); + if ( IsDefined( org ) ) + org Delete(); +} + +do_car_alarm() +{ + if ( IsDefined( self.carAlarm ) ) + return; + self.carAlarm = true; + + if ( !should_do_car_alarm() ) + return; + + self.car_alarm_org = Spawn( "script_model", self.origin ); + self.car_alarm_org Hide(); + self.car_alarm_org PlayLoopSound( CAR_ALARM_ALIAS ); + + level.currentCarAlarms++; + Assert( level.currentCarAlarms <= MAX_SIMULTANEOUS_CAR_ALARMS ); + + self thread car_alarm_timeout(); + + self waittill( "stop_car_alarm" ); + + level.lastCarAlarmTime = GetTime(); + level.currentCarAlarms--; + + self.car_alarm_org StopLoopSound( CAR_ALARM_ALIAS ); + self.car_alarm_org Delete(); +} + +car_alarm_timeout() +{ + self endon( "stop_car_alarm" ); + + // Car alarm only lasts this long until it automatically shuts up + wait CAR_ALARM_TIMEOUT; + + if ( !isdefined( self ) ) + return; + + self thread play_sound( CAR_ALARM_OFF_ALIAS ); + self notify( "stop_car_alarm" ); +} + +should_do_car_alarm() +{ + // If there is already car alarms going off then don't trigger another one + if ( level.currentCarAlarms >= MAX_SIMULTANEOUS_CAR_ALARMS ) + return false; + + // If the player hasn't heard a car alarm yet during this level + timeElapsed = undefined; + if ( !isdefined( level.lastCarAlarmTime ) ) + { + if ( cointoss() ) + return true; + timeElapsed = GetTime() - level.commonStartTime; + } + else + { + timeElapsed = GetTime() - level.lastCarAlarmTime; + } + Assert( IsDefined( timeElapsed ) ); + + // If the player hasn't heard a car alarm in a while then do one + if ( level.currentCarAlarms == 0 && timeElapsed >= NO_CAR_ALARM_MAX_ELAPSED_TIME ) + return true; + + if ( RandomInt( 100 ) <= 33 ) + return true; + + return false; +} + +do_random_dynamic_attachment( tagName, attach_model_1, attach_model_2, clipToRemove ) +{ + Assert( IsDefined( tagName ) ); + Assert( IsDefined( attach_model_1 ) ); + + spawnedModels = []; + + if ( isSP() ) + { + self Attach( attach_model_1, tagName, false ); + if ( IsDefined( attach_model_2 ) && attach_model_2 != "" ) + self Attach( attach_model_2, tagName, false ); + } + else + { + //attach doesn't work in MP so fake it + spawnedModels[ 0 ] = Spawn( "script_model", self GetTagOrigin( tagName ) ); + spawnedModels[ 0 ].angles = self GetTagAngles( tagName ); + spawnedModels[ 0 ] SetModel( attach_model_1 ); + spawnedModels[ 0 ] LinkTo( self, tagName ); + + if ( IsDefined( attach_model_2 ) && attach_model_2 != "" ) + { + spawnedModels[ 1 ] = Spawn( "script_model", self GetTagOrigin( tagName ) ); + spawnedModels[ 1 ].angles = self GetTagAngles( tagName ); + spawnedModels[ 1 ] SetModel( attach_model_2 ); + spawnedModels[ 1 ] LinkTo( self, tagName ); + } + } + + // remove collision that might not be used for this attachment + if ( isdefined( clipToRemove ) ) + { + tagOrg = self getTagOrigin( tagName ); + clip = get_closest_with_targetname( tagOrg, clipToRemove ); + if ( isdefined( clip ) ) + clip delete(); + } + + self waittill( "exploded" ); + + if ( isSP() ) + { + self Detach( attach_model_1, tagName ); + self Attach( attach_model_1 + DESTROYED_ATTACHMENT_SUFFIX, tagName, false ); + + if ( IsDefined( attach_model_2 ) && attach_model_2 != "" ) + { + self Detach( attach_model_2, tagName ); + self Attach( attach_model_2 + DESTROYED_ATTACHMENT_SUFFIX, tagName, false ); + } + } + else + { + spawnedModels[ 0 ] SetModel( attach_model_1 + DESTROYED_ATTACHMENT_SUFFIX ); + if ( IsDefined( attach_model_2 ) && attach_model_2 != "" ) + spawnedModels[ 1 ] SetModel( attach_model_2 + DESTROYED_ATTACHMENT_SUFFIX ); + } +} + +get_closest_with_targetname( origin, targetname ) +{ + closestDist = undefined; + closestEnt = undefined; + ents = getentarray( targetname, "targetname" ); + foreach ( ent in ents ) + { + d = distanceSquared( origin, ent.origin ); + + if ( !isdefined( closestDist ) || ( d < closestDist ) ) + { + closestDist = d; + closestEnt = ent; + } + } + + return closestEnt; +} + +player_touching_post_clip() +{ + post_clip = undefined; + + if ( !IsDefined( self.target ) ) + { + return false; + } + + targets = GetEntArray( self.target, "targetname" ); + foreach ( target in targets ) + { + if ( IsDefined( target.script_destruct_collision ) && target.script_destruct_collision == "post" ) + { + post_clip = target; + break; + } + } + + if ( !IsDefined( post_clip ) ) + { + return false; + } + + player = get_player_touching( post_clip ); + + if ( IsDefined( player ) ) + { + return true; + } + + return false; +} + +get_player_touching( ent ) +{ + foreach ( player in level.players ) + { + if ( !IsAlive( player ) ) + { + continue; + } + + if ( ent IsTouching( player ) ) + { + return player; + } + } + + return undefined; +} + +is_so() +{ + return GetDvar( "specialops" ) == "1"; +} + +destructible_handles_collision_brushes() +{ + targets = GetEntArray( self.target, "targetname" ); + collision_funcs = []; + collision_funcs[ "pre" ] = ::collision_brush_pre_explosion; + collision_funcs[ "post" ] = ::collision_brush_post_explosion; + + foreach ( target in targets ) + { + if ( !isdefined( target.script_destruct_collision ) ) + continue; + self thread [[ collision_funcs[ target.script_destruct_collision ] ]]( target ); + } +} +DYNAMICPATH = 1; + +collision_brush_pre_explosion( clip ) +{ + waittillframeend;// wait for same area post brushes to connect before we disconnect + + if ( isSP() && clip.spawnflags & DYNAMICPATH ) + clip call [[ level.disconnectPathsFunction ]](); + + self waittill( "exploded" ); + + if ( isSP() && clip.spawnflags & DYNAMICPATH ) + clip call [[ level.connectPathsFunction ]](); + + clip Delete(); + +} + +collision_brush_post_explosion( clip ) +{ + clip NotSolid(); + + if ( isSP() && clip.spawnflags & DYNAMICPATH ) + clip call [[ level.connectPathsFunction ]](); + + self waittill( "exploded" ); + waittillframeend;// wait for same area pre brushes to connect before we disconnect + + if ( isSP() ) + { + if( clip.spawnflags & DYNAMICPATH ) + clip call [[ level.disconnectPathsFunction ]](); + + if ( is_so() ) + { + player = get_player_touching( clip ); + if ( isdefined( player ) ) + { + assertex( isdefined( level.func_destructible_crush_player ), "Special Ops requires level.func_destructible_crush_player to be defined." ); + self thread [[ level.func_destructible_crush_player ]]( player ); + } + } + else + { +/# + thread debug_player_in_post_clip( clip ); +#/ + } + } + + clip Solid(); +} + +debug_player_in_post_clip( clip ) +{ +/# + wait( 0.1 ); + player = get_player_touching( clip ); + if ( IsDefined( player ) ) + { + AssertEx( !IsAlive( player ), "Player is in a clip of a destructible, but is still alive. He's either in godmode or we're doing something wrong. Player will be stuck now." ); + } +#/ +} + + +destructible_get_my_breakable_light( range ) +{ + AssertEx( !isdefined( self.breakable_light ), "Tried to define my breakable light twice" ); + + // light = getClosest( self.origin, GetEntArray("light_destructible","targetname") ); + // beh getClosest is SP only.. to lazy to port right now. + // find the nearest light with targetname light_destructible within range and turn it out. scripting stuff in prefabs is still hard. + // + lights = GetEntArray( "light_destructible", "targetname" ); + if ( isSP() )// mp lacks noteworthy powers + { + lights2 = GetEntArray( "light_destructible", "script_noteworthy" ); + lights = array_combine( lights, lights2 ); + } + if ( !lights.size ) + return; + + shortest_distance = range * range; + the_light = undefined; + foreach ( light in lights ) + { + dist = DistanceSquared( self.origin, light.origin ); + if ( dist < shortest_distance ) + { + the_light = light; + shortest_distance = dist; + } + } + + if ( !isdefined( the_light ) ) + return; + + self.breakable_light = the_light; + +} + +break_nearest_light( range ) +{ + if ( !isdefined( self.breakable_light ) ) + return; + + self.breakable_light SetLightIntensity( 0 ); +} + + +debug_radiusdamage_circle( center, radius, maxdamage, mindamage ) +{ + circle_sides = 16; + + angleFrac = 360 / circle_sides; + + // Z circle + circlepoints = []; + for ( i = 0; i < circle_sides; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + xAdd; + y = center[ 1 ] + yAdd; + z = center[ 2 ]; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + + // X circle + circlepoints = []; + for ( i = 0; i < circle_sides; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ]; + y = center[ 1 ] + xAdd; + z = center[ 2 ] + yAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + + // Y circle + circlepoints = []; + for ( i = 0; i < circle_sides; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + yAdd; + y = center[ 1 ]; + z = center[ 2 ] + xAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + + // draw center and range with values + Print3d( center, maxdamage, ( 1, 1, 1 ), 1, 1, 100 ); + Print3d( center + ( radius, 0, 0 ), mindamage, ( 1, 1, 1 ), 1, 1, 100 ); +} + +debug_circle_drawlines( circlepoints, duration, color, center ) +{ + Assert( IsDefined( center ) ); + for ( i = 0; i < circlepoints.size; i++ ) + { + start = circlepoints[ i ]; + if ( i + 1 >= circlepoints.size ) + end = circlepoints[ 0 ]; + else + end = circlepoints[ i + 1 ]; + + thread debug_line( start, end, duration, color ); + thread debug_line( center, start, duration, color ); + } +} + +debug_line( start, end, duration, color ) +{ + if ( !isdefined( color ) ) + color = ( 1, 1, 1 ); + + for ( i = 0; i < ( duration * 20 ); i++ ) + { + Line( start, end, color ); + wait 0.05; + } +} + +spotlight_tag_origin_cleanup( tag_origin ) +{ + tag_origin endon( "death" ); + level waittill( "new_destructible_spotlight" ); + tag_origin Delete(); +} + +spotlight_fizzles_out( action_v, eModel, damageType, partIndex, tag_origin ) +{ + + level endon( "new_destructible_spotlight" ); + thread spotlight_tag_origin_cleanup( tag_origin ); + + maxVal = action_v[ "spotlight_brightness" ]; + //noself_func( "setsaveddvar", "r_spotlightbrightness", maxVal ); + + wait( RandomFloatRange( 2, 5 ) ); + + //IW6 code to make light fizzle/blink/fade goes here. + + destructible_fx_think( action_v, eModel, damageType, partIndex ); + level.destructible_spotlight Delete(); + tag_origin Delete(); +} + +destructible_spotlight_think( action_v, eModel, damageType, partIndex ) +{ + // spotlights are MP only + if ( !isSP() ) + return; + + if ( !isdefined( self.breakable_light ) ) + return; + + emodel self_func( "startignoringspotLight" ); + + if ( !isdefined( level.destructible_spotlight ) ) + { + level.destructible_spotlight = spawn_tag_origin(); + fx = getfx( action_v[ "spotlight_fx" ] ); + PlayFXOnTag( fx, level.destructible_spotlight, "tag_origin" ); + } + + //self.breakable_light thread maps\_debug::drawForwardForever( 200, (1,0,0) ); + //level.destructible_spotlight thread maps\_debug::drawForwardForever( 200, (1,1,0) ); + + level notify( "new_destructible_spotlight" ); + + level.destructible_spotlight Unlink(); + + tag_origin = spawn_tag_origin(); + tag_origin LinkTo( self, action_v[ "spotlight_tag" ], ( 0, 0, 0 ), ( 0, 0, 0 ) ); + + //eModel thread maps\_debug::drawTagForever( action_v[ "spotlight_tag" ] ); + + level.destructible_spotlight.origin = self.breakable_light.origin; + level.destructible_spotlight.angles = self.breakable_light.angles; + level.destructible_spotlight thread spotlight_fizzles_out( action_v, eModel, damageType, partIndex, tag_origin ); + + wait( 0.05 );// Wait for the spawned tag_origin to get to the right place before linking + if ( IsDefined( tag_origin ) ) + { + // can be deleted during wait + level.destructible_spotlight LinkTo( tag_origin ); + } + +} + +is_valid_damagetype( damageType, v, idx, groupNum ) +{ + valid_damagetype = undefined; + if ( IsDefined( v[ "fx_valid_damagetype" ] ) ) + valid_damagetype = v[ "fx_valid_damagetype" ][ groupNum ][ idx ]; + + if ( !isdefined( valid_damagetype ) ) + return true; + + return IsSubStr( valid_damagetype, damageType ); +} + +destructible_sound_think( action_v, eModel, damageType, groupNum ) +{ + if ( isdefined( self.exploded ) ) + return undefined; // Boon Nov 2012: This seems wrong. If there are no sounds defined, we lose our record of what group we're using? + + if ( !isDefined( action_v[ "sound" ] ) ) + return undefined; + + if ( !isdefined( groupNum ) ) + groupNum = 0; + + // Boon Nov 2012: I don't think it should be compulsory to have a sound for every effect. + //assert( isDefined( action_v[ "sound" ][ groupNum ] ) ); + if ( !IsDefined( action_v[ "sound" ][ groupNum ] ) ) + return undefined; + + for ( i = 0; i < action_v[ "sound" ][ groupNum ].size; i++ ) + { + validSoundCause = self isValidSoundCause( "soundCause", action_v, i, damageType, groupNum ); + if ( !validSoundCause ) + continue; + + soundAlias = action_v[ "sound" ][ groupNum ][ i ]; + soundTagName = action_v[ "tagName" ]; //chad - dont think I need a groupnum index here, but now we probably can't support playing sounds on multiple tags within one group + eModel thread play_sound( soundAlias, soundTagName ); + } + + return groupNum; +} + +destructible_fx_kill_state_wait( part_indx ) +{ + // Wait til the current part changes state- or the entire destructible dies to kill any fx + final_global_state_index = level.destructible_type[self.destructibleInfo].parts[0].size - 1; + + self endon( ( "FX_State_Change_Kill" + part_indx ) ); + //self endon( ( "FX_State_Change_Kill0" ) ); + for(;;) + { + curr_global_state_index = -1; + if (IsDefined(self.destructible_parts[ 0 ].v[ "currentState" ])) + curr_global_state_index = self.destructible_parts[ 0 ].v[ "currentState" ]; + if( curr_global_state_index == final_global_state_index ) return 0; + waitframe(); + } +} + + +// +// spawnImmediate_think detects the state change of the destructible & will attempt +// + +destructible_fx_spawn_think( eModel, fxid, fx_tag , i, usetagangles, stateChangeKill ) +{ + waittillframeend; + + + if( !IsDefined( stateChangeKill ) ) stateChangeKill = 0; + fxObj = undefined; + fx_spawn_obj = undefined; + if ( IsDefined( fx_tag ) ) + { + if ( usetagangles ) + { + + PlayFXOnTag( fxid, eModel, fx_tag ); + wait(.05); // Will not spawn & kill on the same frame + if( stateChangeKill == 1 || stateChangeKill == 2 ) + { + self destructible_fx_kill_state_wait( i ); + if( stateChangeKill == 1 ) StopFXOnTag( fxid, eModel, fx_tag ); + else KillFXOnTag( fxid, eModel, fx_tag ); + } + } + else + { + fxOrigin = eModel GetTagOrigin( fx_tag ); + forward = ( 0, 0, 100 ); + if( stateChangeKill == 1 || stateChangeKill == 2 ) + { + fx_spawn_obj = SpawnFx( fxid, fxOrigin, forward ); + fxObj = TriggerFX( fx_spawn_obj , 0.01); + } + else fxObj = playFX( fxid, fxOrigin, forward ); + wait(.05); // Will not spawn & kill on the same frame + if( stateChangeKill == 1 || stateChangeKill == 2 ) + { + self destructible_fx_kill_state_wait( i ); + if( stateChangeKill == 1 ) + { + fx_spawn_obj Delete(); + } + else if( stateChangeKill == 2 ) + { + SetFXKillOnDelete( fx_spawn_obj, true ); + wait( .05 ); + fx_spawn_obj delete(); + } + } + } + } + else + { + fxOrigin = eModel.origin; + forward = ( 0, 0, 100 ); + if( stateChangeKill == 1 || stateChangeKill == 2 ) + { + fx_spawn_obj = SpawnFx( fxid, fxOrigin, forward ); + fxObj = TriggerFX( fx_spawn_obj , 0.01); + } + else fxObj = playFX( fxid, fxOrigin, forward ); + wait(.05); // Will not spawn & kill on the same frame + if( stateChangeKill == 1 || stateChangeKill == 2 ) + { + self destructible_fx_kill_state_wait( i ); + if( stateChangeKill == 1 ) + { + fx_spawn_obj Delete(); + } + else if( stateChangeKill == 2 ) + { + SetFXKillOnDelete( fx_spawn_obj, true ); + wait( .05 ); + fx_spawn_obj delete(); + } + } + } + +} + + +destructible_fx_spawnImmediate() +{ + //wait( 1.0 ); // Wait for everything else to get started + if ( !isdefined( level.destructible_type[ self.destructibleInfo].parts ) ) + return; + eModel = get_dummy(); + + for ( i = 0; i < level.destructible_type[ self.destructibleInfo].parts.size; i++ ) + { + for ( j = 0; j < level.destructible_type[ self.destructibleInfo].parts[ i ].size; j++ ) + { + + part = level.destructible_type[ self.destructibleInfo].parts[ i ][ j ]; + + if ( IsDefined( part.v[ "fx_filename" ] ) ) + { + for ( g = 0; g < part.v[ "fx_filename" ].size; g++ ) + { + // for multiple checks on fx when doing conditional fx playing + fx_filenames = part.v[ "fx_filename" ][ g ]; + fx_tags = part.v[ "fx_tag" ][ g ]; + fx_spawn_immediate_array = part.v[ "spawn_immediate" ][g]; + + if ( IsDefined( fx_filenames ) && IsDefined( fx_spawn_immediate_array ) ) + { + assert( IsDefined( fx_tags ) ); + + for ( idx = 0; idx < fx_filenames.size; idx++ ) + { + if( fx_spawn_immediate_array[ idx ] == true ) + { + stateChangeKill = part.v[ "state_change_kill" ][ g ][ idx ]; + fxid = level.destructible_type[ self.destructibleInfo ].parts[ i ][ j ].v[ "fx" ][ g ][ idx ]; + fx_tag = fx_tags[idx]; + fx_filename = fx_filenames[idx]; + usetagangles = level.destructible_type[ self.destructibleInfo ].parts[ i ][ j ].v[ "fx_useTagAngles" ][ g ][ idx ]; + self thread destructible_fx_spawn_think( eModel, fxid, fx_tag , i, usetagangles, stateChangeKill ); + } + } + } + } + } + + } + } +} + + +destructible_fx_think( action_v, eModel, damageType, partIndex, groupNum ) +{ + if ( !isdefined( action_v[ "fx" ] ) ) + return undefined; + + if ( !isdefined( groupNum ) ) + groupNum = randomInt( action_v[ "fx_filename" ].size ); + + if ( !isDefined( action_v[ "fx" ][ groupNum ] ) ) + { + println( "^1destructible tried to use custom groupNum for FX but that group didn't exist" ); + groupNum = randomInt( action_v[ "fx_filename" ].size ); + } + + assert( isDefined( action_v[ "fx" ][ groupNum ] ) ); + + fx_size = action_v[ "fx_filename" ][ groupNum ].size; + + + + for ( idx = 0; idx < fx_size; idx++ ) + { + if ( !is_valid_damagetype( damageType, action_v, idx, groupNum ) ) + continue; + + // Ignoring all fx that are spawned immediately + if ( action_v[ "spawn_immediate" ][ groupNum ][ idx ] == true ) + continue; + + fx = action_v[ "fx" ][ groupNum ][ idx ]; + + stateChangeKill = action_v[ "state_change_kill" ][ groupNum ][ idx ]; + + if ( IsDefined( action_v[ "fx_tag" ][ groupNum ][ idx ] ) ) + { + fx_tag = action_v[ "fx_tag" ][ groupNum ][ idx ]; + self notify( "FX_State_Change" + partIndex ); + + if ( action_v[ "fx_useTagAngles" ][ groupNum ][ idx ] ) + { + self thread destructible_fx_spawn_think( eModel, fx, fx_tag , partIndex, true, stateChangeKill ); + } + else + { + self thread destructible_fx_spawn_think( eModel, fx, fx_tag , partIndex, false, stateChangeKill ); + } + } + else + { + self thread destructible_fx_spawn_think( eModel, fx, undefined , partIndex, false, stateChangeKill ); + } + } + + return groupNum; +} + +destructible_animation_think( action_v, eModel, damageType, partIndex ) +{ + if ( IsDefined( self.exploded ) ) + return undefined; + + if ( !isdefined( action_v[ "animation" ] ) ) + return undefined; + + if ( IsDefined( self.no_destructible_animation ) ) + return undefined; + + if ( IsDefined( action_v[ "randomly_flip" ] ) && !isdefined( self.script_noflip ) ) + { + if ( cointoss() ) + { + // flip it around for randomness + self.angles += ( 0, 180, 0 ); + } + } + + // this stuff is SP only + if ( IsDefined( action_v[ "spotlight_tag" ] ) ) + { + thread destructible_spotlight_think( action_v, eModel, damageType, partIndex ); + wait( 0.05 ); + } + + array = random( action_v[ "animation" ] ); + + animName = array[ "anim" ]; + animTree = array[ "animTree" ]; + groupNum = array[ "groupNum" ]; + mpAnim = array[ "mpAnim" ]; + + maxStartDelay = array[ "maxStartDelay" ]; + animRateMin = array[ "animRateMin" ]; + animRateMax = array[ "animRateMax" ]; + + if ( !isdefined( animRateMin ) ) + animRateMin = 1.0; + if ( !isdefined( animRateMax ) ) + animRateMax = 1.0; + if ( animRateMin == animRateMax ) + animRate = animRateMin; + else + animRate = RandomFloatRange( animRateMin, animRateMax ); + + vehicle_dodge_part_animation = array[ "vehicle_exclude_anim" ]; + + if ( self.code_classname == "script_vehicle" && vehicle_dodge_part_animation ) + return undefined; + + eModel self_func( "useanimtree", animTree ); + + animType = array[ "animType" ]; + + if ( !isdefined( self.animsApplied ) ) + self.animsApplied = []; + self.animsApplied[ self.animsApplied.size ] = animName; + + if ( IsDefined( self.exploding ) ) + self clear_anims( eModel ); + + if ( IsDefined( maxStartDelay ) && maxStartDelay > 0 ) + wait RandomFloat( maxStartDelay ); + + // Multiplayer animations work now + if ( !isSP() ) + { + if ( IsDefined( mpAnim ) ) + self self_func( "scriptModelPlayAnim", mpAnim ); + return groupNum; + } + + if ( animType == "setanim" ) + { + eModel self_func( "setanim", animName, 1.0, 1.0, animRate ); + return groupNum; + } + + if ( animType == "setanimknob" ) + { + eModel self_func( "setanimknob", animName, 1.0, 0, animRate ); + return groupNum; + } + + AssertMsg( "Tried to play an animation on a destructible with an invalid animType: " + animType ); + return undefined; +} + +clear_anims( eModel ) +{ + //clear all previously blended anims if the vehicle is exploding so the explosion doesn't have to blend with anything + if ( IsDefined( self.animsApplied ) ) + { + foreach ( animation in self.animsApplied ) + { + if ( isSP() ) + eModel self_func( "clearanim", animation, 0 ); + else + eModel self_func( "scriptModelClearAnim" ); + } + } +} + +init_destroyed_count() +{ + level.destroyedCount = 0; + level.destroyedCountTimeout = 0.5; + + if ( isSP() ) + level.maxDestructions = 20; + else + level.maxDestructions = 2; + +} + +add_to_destroyed_count() +{ + level.destroyedCount++; + + wait( level.destroyedCountTimeout ); + + level.destroyedCount--; + + Assert( level.destroyedCount >= 0 ); +} + +get_destroyed_count() +{ + return( level.destroyedCount ); +} + +get_max_destroyed_count() +{ + return( level.maxDestructions ); +} + + +init_destructible_frame_queue() +{ + level.destructibleFrameQueue = []; +} + +add_destructible_to_frame_queue( destructible, partInfo, damage ) +{ + entNum = self GetEntityNumber(); + + if ( !isDefined( level.destructibleFrameQueue[ entNum ] ) ) + { + level.destructibleFrameQueue[ entNum ] = SpawnStruct(); + level.destructibleFrameQueue[ entNum ].entNum = entNum; + level.destructibleFrameQueue[ entNum ].destructible = destructible; + level.destructibleFrameQueue[ entNum ].totalDamage = 0; + level.destructibleFrameQueue[ entNum ].nearDistance = 9999999; + level.destructibleFrameQueue[ entNum ].fxCost = 0; + } + + level.destructibleFrameQueue[ entNum ].fxCost += partInfo.v[ "fxcost" ]; + + level.destructibleFrameQueue[ entNum ].totalDamage += damage; + if ( partInfo.v[ "distance" ] < level.destructibleFrameQueue[ entNum ].nearDistance ) + level.destructibleFrameQueue[ entNum ].nearDistance = partInfo.v[ "distance" ]; + + thread handle_destructible_frame_queue(); +} + + +handle_destructible_frame_queue() +{ + level notify( "handle_destructible_frame_queue" ); + level endon( "handle_destructible_frame_queue" ); + + wait( 0.05 ); + + currentQueue = level.destructibleFrameQueue; + level.destructibleFrameQueue = []; + + sortedQueue = sort_destructible_frame_queue( currentQueue ); + + for ( i = 0; i < sortedQueue.size; i++ ) + { + if ( get_destroyed_count() < get_max_destroyed_count() ) + { + if ( sortedQueue[ i ].fxCost ) + thread add_to_destroyed_count(); + + sortedQueue[ i ].destructible notify( "queue_processed", true ); + } + else + { + sortedQueue[ i ].destructible notify( "queue_processed", false ); + } + } +} + + +sort_destructible_frame_queue( unsortedQueue ) +{ + sortedQueue = []; + foreach ( destructibleInfo in unsortedQueue ) + sortedQueue[ sortedQueue.size ] = destructibleInfo; + + // insertion sort + for ( i = 1; i < sortedQueue.size; i++ ) + { + queueStruct = sortedQueue[ i ]; + + for ( j = i - 1; j >= 0 && get_better_destructible( queueStruct, sortedQueue[ j ] ) == queueStruct; j-- ) + sortedQueue[ j + 1 ] = sortedQueue[ j ]; + + sortedQueue[ j + 1 ] = queueStruct; + } + + return sortedQueue; +} + + +get_better_destructible( destructibleInfo1, destructibleInfo2 ) +{ + // this is very basic; we can also account for distance, fxcost, etc... if we need to + if ( destructibleInfo1.totalDamage > destructibleInfo2.totalDamage ) + return destructibleInfo1; + else + return destructibleInfo2; +} + + +get_part_FX_cost_for_action_state( partIndex, actionStateIndex ) +{ + fxCost = 0; + + if ( !isDefined( level.destructible_type[ self.destructibleInfo ].parts[ partIndex ][ actionStateIndex ] ) ) + return fxCost; + + action_v = level.destructible_type[ self.destructibleInfo].parts[ partIndex ][ actionStateIndex ].v; + + if ( IsDefined( action_v[ "fx" ] ) ) + { + foreach ( fxCostObj in action_v[ "fx_cost" ] ) + { + foreach ( fxCostVal in fxCostObj ) + fxCost += fxCostVal; + } + } + + return fxCost; +} + +// ************************************* +// DOT - **TODO for iw6 move into seperate .gsc +// ************************************* + +initDOT( type ) +{ + AssertEx( IsDefined( type ), "Must specify a type of 'poision'" ); + + if ( !flag_exist( "FLAG_DOT_init" ) ) + { + flag_init( "FLAG_DOT_init" ); + //level._dots = []; + flag_set( "FLAG_DOT_init" ); + } + + type = ToLower( type ); + + switch ( type ) + { + case "poison": + if ( !flag_exist( "FLAG_DOT_poison_init" ) ) + { + flag_init( "FLAG_DOT_poison_init" ); + flag_set( "FLAG_DOT_poison_init" ); + } + break; + default: + AssertMsg( "Must specify a type of 'poison'" ); + } +} + +// Not sure if we will ever get support for this +//createDOT_sphere(){} + +createDOT() +{ + dot = SpawnStruct(); + dot.ticks = []; + + return dot; +} + +createDOT_radius( origin, spawnflags, radius, height ) +{ + AssertEx( IsDefined( origin ), "Must specify a origin for the DOT" ); + AssertEx( IsDefined( spawnflags ), "Must specify spawnflags for the DOT" ); + AssertEx( IsDefined( radius ), "Must specify a radius for the DOT" ); + AssertEx( IsDefined( height ), "Must specify a height for the DOT" ); + + dot = SpawnStruct(); + + dot.type = "trigger_radius"; + dot.origin = origin; + dot.spawnflags = spawnflags; + dot.radius = radius; + dot.minRadius = radius; + dot.maxRadius = radius; + dot.height = height; + + dot.ticks = []; + + return dot; +} + +// There is a code request for this ... so stubbing this in for now +/* +createDOT_rect( origin, spawnflags, length, width, height ){} +*/ +/* +addDOT_radius( origin, spawnflags, radius, height ) +{ + AssertEx( IsDefined( origin ), "Must specify a origin for the DOT" ); + AssertEx( IsDefined( spawnflags ), "Must specify spawnflags for the DOT" ); + AssertEx( IsDefined( radius ), "Must specify a radius for the DOT" ); + AssertEx( IsDefined( height ), "Must specify a height for the DOT" ); + + dot = SpawnStruct(); + + dot.origin = origin; + dot.spawnflags = spawnflags; + dot.radius = radius; + dot.minRadius = radius; + dot.maxRadius = radius; + dot.height = height; + + dot.ticks = []; + + self.dot = dot; + dot.parent = self; + + return dot; +} +*/ + +setDOT_origin( origin ) +{ + AssertEx( IsDefined( origin ), "Must specify a origin" ); + + self.origin = origin; +} + +setDOT_radius( minRadius, maxRadius ) +{ + // Check if this is a valid call for self + + if ( IsDefined( self.classname ) && self.classname != "trigger_radius" ) + AssertMsg( "You can only use setDOT_radius on trigger_radius" ); + + AssertEx( IsDefined( minRadius ), "Must define minRadius" ); + + if ( !IsDefined( maxRadius ) ) + maxRadius = minRadius; + + AssertEx( maxRadius >= minRadius, "maxRadius must be greater than minRadius" ); + AssertEx( self.radius >= maxRadius, "radius on trigger must be greater than or equal to maxRadius" ); + + self.minRadius = minRadius; + self.maxRadius = maxRadius; +} + +setDOT_height( minHeight, maxHeight ) +{ + // Check if this is a valid call for self + + if ( IsDefined( self.classname ) && IsSubStr( self.classname, "trigger" ) ) + AssertMsg( "You can only use setDOT_height on triggers" ); +} + +setDOT_onTick( delay, interval, duration, minDamage, maxDamage, falloff, type, affected ) +{ + if ( IsDefined( delay ) ) + AssertEx( delay >= 0, "Must specify a delay >= 0" ); + else + delay = 0; + AssertEx( IsDefined( interval ) && interval > 0 , "Must specify an interval > 0" ); + AssertEx( IsDefined( duration ) && duration > 0, "Must specify a duration > 0" ); + AssertEx( duration > interval, "duration must be > interval" ); + AssertEx( IsDefined( minDamage ) && minDamage >= 0, "Must specify a minDamage >= 0" ); + AssertEx( IsDefined( maxDamage ) && maxDamage > 0, "Must specify a maxDamage > 0" ); + AssertEx( IsDefined( falloff ), "Must specify a falloff of 0 or 1" ); + AssertEx( IsDefined( type ), "Must specify a type of 'normal' or 'poison'" ); + AssertEx( IsDefined( affected ), "Must specify a type of 'player'" ); + + type = ToLower( type ); + affected = ToLower( affected ); + + index = self.ticks.size; + + self.ticks[ index ] = SpawnStruct(); + self.ticks[ index ].enable = 0; + self.ticks[ index ].delay = delay; + self.ticks[ index ].interval = interval; + self.ticks[ index ].duration = duration; + self.ticks[ index ].minDamage = minDamage; + self.ticks[ index ].maxDamage = maxDamage; + + switch ( falloff ) + { + case 0: + case 1: + break; + default: + AssertMsg( "Must specify a falloff of 0 or 1" ); + } + + self.ticks[ index ].falloff = falloff; + self.ticks[ index ].startTime = 0; + + switch ( type ) + { + case "normal": + break; + case "poison": + switch( affected ) + { + case "player": + self.ticks[ index ].type = type; + self.ticks[ index ].affected = affected; + self.ticks[ index ].onEnterFunc = ::onEnterDOT_poisonDamagePlayer; + self.ticks[ index ].onExitFunc = ::onExitDOT_poisonDamagePlayer; + self.ticks[ index ].onDeathFunc = ::onDeathDOT_poisonDamagePlayer; + break; + default: + AssertMsg( "Must specify the affected. Valid types are 'player'" ); + } + break; + default: + AssertMsg( "Must specify a type. Valid types are 'normal' or 'poision'" ); + } +} + + +/* +removeDOT_onTick( funcs ){} +*/ + +buildDOT_onTick( duration, affected ) +{ + AssertEx( IsDefined( duration ), "Must specify a duration > 0" ); + AssertEx( IsDefined( affected ), "Must specify a type of 'player'" ); + + affected = ToLower( affected ); + + index = self.ticks.size; + + self.ticks[ index ] = SpawnStruct(); + self.ticks[ index ].duration = duration; + self.ticks[ index ].delay = 0; + self.ticks[ index ].onEnterFunc = ::onEnterDOT_buildFunc; + self.ticks[ index ].onExitFunc = ::onExitDOT_buildFunc; + self.ticks[ index ].onDeathFunc = ::onDeathDOT_buildFunc; + + switch( affected ) + { + case "player": + self.ticks[ index ].affected = affected; + break; + default: + AssertMsg( "Must specify the affected. Valid types are 'player'" ); + } +} + +buildDOT_startLoop( count ) +{ + AssertEx( IsDefined( count ), "Must specify a count >= 0" ); + AssertEx( IsDefined( self.ticks ) && self.ticks.size >= 0, "Must call buildDOT_onTick first" ); + + index = self.ticks.size - 1; + + if ( !IsDefined( self.ticks[ index ].statements ) ) + self.ticks[ index ].statements = []; + + statementIndex = self.ticks[ index ].statements.size; + + self.ticks[ index ].statements = []; + self.ticks[ index ].statements[ "vars" ] = []; + self.ticks[ index ].statements[ "vars" ][ "count" ] = count; +} + +buildDOT_damage( minDamage, maxDamage, falloff, damageFlag, meansOfDeath, weapon ) +{ + AssertEx( IsDefined( minDamage ), "" ); + AssertEx( IsDefined( maxDamage ), "" ); + AssertEx( IsDefined( falloff ), "" ); + AssertEx( IsDefined( damageFlag ), "" ); + AssertEx( IsDefined( meansOfDeath ), "" ); + AssertEx( IsDefined( weapon ), "" ); + AssertEx( IsDefined( self.ticks ), "Must call buildDOT_startLoop first" ); + + tickIndex = self.ticks.size - 1; + + AssertEx( IsDefined( self.ticks[ tickIndex ] ) && + IsDefined( self.ticks[ tickIndex ].statements ), "Must call buildDOT_startLoop first" ); + + if ( !IsDefined( self.ticks[ tickIndex ].statements[ "actions" ] ) ) + self.ticks[ tickIndex ].statements[ "actions" ] = []; + + actionIndex = self.ticks[ tickIndex ].statements[ "actions" ].size; + + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ] = []; + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ][ "vars" ] = [ minDamage, maxDamage, falloff, damageFlag, meansOfDeath, weapon ]; + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ][ "func" ] = ::doBuildDOT_damage; +} + +buildDOT_wait( time ) +{ + AssertEx( IsDefined( time ), "Must specify time >= 0" ); + AssertEx( IsDefined( self.ticks ), "Must call buildDOT_startLoop first" ); + + tickIndex = self.ticks.size - 1; + + AssertEx( IsDefined( self.ticks[ tickIndex ] ) && + IsDefined( self.ticks[ tickIndex ].statements ), "Must call buildDOT_startLoop first" ); + + if ( !IsDefined( self.ticks[ tickIndex ].statements[ "actions" ] ) ) + self.ticks[ tickIndex ].statements[ "actions" ] = []; + + actionIndex = self.ticks[ tickIndex ].statements[ "actions" ].size; + + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ] = []; + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ][ "vars" ] = [ time ]; + self.ticks[ tickIndex ].statements[ "actions" ][ actionIndex ][ "func" ] = ::doBuildDOT_wait; +} + +onEnterDOT_buildFunc( idx, trigger ) +{ + AssertEx( IsDefined( idx ), "Must specify an index for this function" ); + AssertEx( IsDefined( trigger ), "trying to run tick function on DOT that has been removed or is undefined" ); + + entNum = trigger GetEntityNumber(); + + trigger endon( "death" ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum ); + + self endon( "disconnect" ); + self endon( "game_ended" ); + self endon( "death" ); + self endon( "LISTEN_exit_dot_" + entNum ); + + entNum = undefined; + statements = trigger.ticks[ idx ].statements; + + if ( !IsDefined( statements ) || + !IsDefined( statements[ "vars" ] ) || + !IsDefined( statements[ "vars" ][ "count" ] ) || + !IsDefined( statements[ "actions" ] ) ) + return; + + count = statements[ "vars" ][ "count" ]; + actions = statements[ "actions" ]; + statements = undefined; + + // count = 0 runs loop forever + // count >= 1 runs the loop count number of times + + for ( i = 1; i <= count || count == 0; i-- ) + { + foreach ( action in actions ) + { + vars = action[ "vars" ]; + func = action[ "func" ]; + + self [[ func ]]( idx, trigger, vars ); + } + } +} + +onExitDOT_buildFunc( idx, trigger ) +{ + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + + trigger notify( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); +} + +onDeathDOT_buildFunc( idx, trigger ){} + +doBuildDOT_damage( idx, trigger, vars ) +{ + AssertEx( IsDefined( idx ), "Must specify an index >= 0" ); + AssertEx( IsDefined( trigger ), "Must specify a trigger" ); + AssertEx( IsDefined( vars ), "Must specify vars" ); + + minDamage = vars[ 0 ]; + maxDamage = vars[ 1 ]; + falloff = vars[ 2 ]; + damageFlag = vars[ 3 ]; + meansOfDeath = vars[ 4 ]; + weapon = vars[ 5 ]; + + self thread [[ level.callbackPlayerDamage ]]( + trigger, // eInflictor The entity that causes the damage.( e.g. a turret ) + trigger, // eAttacker The entity that is attacking. + maxDamage, // iDamage Integer specifying the amount of damage done + damageFlag, // iDFlags Integer specifying flags that are to be applied to the damage + meansOfDeath, // sMeansOfDeath Integer specifying the method of death + weapon, // sWeapon The weapon number of the weapon used to inflict the damage + trigger.origin, // vPoint The point the damage is from? + ( 0,0,0 ) - trigger.origin,// vDir The direction of the damage + "none", // sHitLoc The location of the hit + 0 // psOffsetTime The time offset for the damage + ); +} + +doBuildDOT_wait( idx, trigger, vars ) +{ + AssertEx( IsDefined( idx ), "Must specify an index >= 0" ); + AssertEx( IsDefined( trigger ), "Must specify a trigger" ); + AssertEx( IsDefined( vars ), "Must specify vars" ); + + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + + trigger endon( "death" ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum ); + trigger notify( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); + + self endon( "disconnect" ); + self endon( "game_ended" ); + self endon( "death" ); + self endon( "LISTEN_exit_dot_" + entNum ); + + entNum = undefined; + playerNum = undefined; + + wait vars[ 0 ]; +} + +startDOT_group( dots ) +{ + AssertEx( IsDefined( dots ), "Must specify an array of dots to start" ); + + triggers = []; + + foreach ( dot in dots ) + { + // Spawn the appropriate dot.type aka trigger + + trigger = undefined; + + switch ( dot.type ) + { + case "trigger_radius": + trigger = Spawn( "trigger_radius", dot.origin, dot.spawnflags, dot.radius, dot.height ); + AssertEx( IsDefined( trigger ), "Could not spawn a trigger, too many entities" ); + + trigger.minRadius = dot.minRadius; + trigger.maxRadius = dot.maxRadius; + trigger.ticks = dot.ticks; + triggers[ triggers.size ] = trigger; + break; + default: + AssertMsg( ".type for DOT is not supported" ); + } + + // Link to parent if it was defined + + if ( IsDefined( dot.parent ) ) + { + trigger LinkTo( dot.parent ); + dot.parent.dot = trigger; + } + + ticks = trigger.ticks; + + // Initialize ticks + + foreach ( tick in ticks ) + tick.startTime = GetTime(); + + foreach ( tick in ticks ) + if ( !tick.delay ) + tick.enable = 1; + + // Check if there is a tick on player + + foreach ( tick in ticks ) + { + if ( IsSubStr( tick.affected, "player" ) ) + { + trigger.onPlayer = 1; + break; + } + } + } + + // Populate a group list for each trigger. Exclude self from list + + foreach ( trigger in triggers ) + { + trigger.DOT_group = []; + + foreach ( _trigger in triggers ) + { + if ( trigger == _trigger ) + continue; + trigger.DOT_group[ trigger.DOT_group.size ] = _trigger; + } + } + + // Start DOT on player + + foreach ( trigger in triggers ) + if ( trigger.onPlayer ) + trigger thread startDOT_player(); + + // Monitor DOTs + + foreach ( trigger in triggers ) + trigger thread monitorDOT(); +} + +startDOT_player() +{ + self thread triggerTouchThink( ::onEnterDOT_player, ::onExitDOT_player ); +} + +// Check to see when we should delete a DOT + +monitorDOT() +{ + startTime = GetTime(); + + for ( ; IsDefined( self ); wait 0.05 ) + { + foreach ( i, tick in self.ticks ) + { + if ( IsDefined( tick ) && GetTime() - startTime >= tick.duration * 1000 ) + { + entNum = self GetEntityNumber(); + self notify( "LISTEN_kill_tick_" + i + "_" + entNum ); + self.ticks[ i ] = undefined; + } + } + + if ( !self.ticks.size ) + break; + } + + if ( IsDefined( self ) ) + { + foreach ( tick in self.ticks ) + self [[ tick.onDeathFunc ]](); + + self notify( "death" ); + self Delete(); + } +} + +onEnterDOT_player( trigger ) +{ + Assert( IsDefined( trigger ) ); + + entNum = trigger GetEntityNumber(); + + self notify( "LISTEN_enter_dot_" + entNum ); + + foreach ( i, tick in trigger.ticks ) + if ( !tick.enable ) + self thread doDOT_delayFunc( i, trigger, tick.delay, tick.onEnterFunc ); + + foreach ( i, tick in trigger.ticks ) + if ( tick.enable && tick.affected == "player" ) + self thread [[ tick.onEnterFunc ]]( i, trigger ); +} + +onExitDOT_player( trigger ) +{ + Assert( IsDefined( trigger ) ); + + entNum = trigger GetEntityNumber(); + + self notify( "LISTEN_exit_dot_" + entNum ); + + foreach ( i, tick in trigger.ticks ) + if ( tick.enable && tick.affected == "player" ) + self thread [[ tick.onExitFunc ]]( i, trigger ); +} + +doDOT_delayFunc( idx, trigger, delay, func ) +{ + Assert( IsDefined( trigger ) ); + + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); + + self endon( "disconnect" ); + self endon( "game_ended" ); + self endon( "death" ); + + self notify( "LISTEN_exit_dot_" + entNum ); + + entNum = undefined; + playerNum = undefined; + + wait delay; + + self thread [[ func ]]( idx, trigger ); +} + +/* +soundWatcher( entNum ) +{ + Assert( IsDefined( entNum ) ); + + self waittill_any( "death", "LISTEN_exit_dot_ + entNum ); + + self StopLoopSound(); +} +*/ + +// This is a predefined DOT function + +onEnterDOT_poisonDamagePlayer( idx, trigger ) +{ + AssertEx( IsDefined( idx ), "Must specify an index for this function" ); + AssertEx( IsDefined( trigger ), "trying to run tick function on DOT that has been removed or is undefined" ); + + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + + trigger endon( "death" ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); + + self endon( "disconnect" ); + self endon( "game_ended" ); + self endon( "death" ); + self endon( "LISTEN_exit_dot_" + entNum ); + + // Setup vars to keep track of number times player has been damaged by the trigger + // For now this damage function is almost the same as the maps\mp\_radiatoin::radiationEffect + + if ( !IsDefined( self.onEnterDOT_poisonDamageCount ) ) + self.onEnterDOT_poisonDamageCount = []; + if ( !IsDefined( self.onEnterDOT_poisonDamageCount[ idx ] ) ) + self.onEnterDOT_poisonDamageCount[ idx ] = []; + self.onEnterDOT_poisonDamageCount[ idx ][ entNum ] = 0; + + //self thread soundWatcher( entNum ); + + damageMultiplier = ter_op( isSP(), 1.5, 1 ); + + for ( ; IsDefined( trigger ) && IsDefined( trigger.ticks[ idx ] ); wait trigger.ticks[ idx ].interval ) + { + self.onEnterDOT_poisonDamageCount[ idx ][ entNum ]++; + + switch ( self.onEnterDOT_poisonDamageCount[ idx ][ entNum ] ) + { + case 1: + self ViewKick( 1, self.origin ); + break; + case 3: + self ShellShock( "mp_radiation_low", 4 ); + + //self ViewKick( 3, self.origin ); + self doDOT_poisonDamage( trigger, damageMultiplier * 2 ); + break; + case 4: + self ShellShock( "mp_radiation_med", 5 ); + + //self ViewKick( 15, self.origin ); + self thread doDOT_poisonBlackout( idx, trigger ); + self doDOT_poisonDamage( trigger, damageMultiplier * 2 ); + break; + case 6: + self ShellShock( "mp_radiation_high", 5 ); + + //self ViewKick( 75, self.origin ); + self doDOT_poisonDamage( trigger, damageMultiplier * 2 ); + break; + case 8: + self ShellShock( "mp_radiation_high", 5 ); + + //self ViewKick( 127, self.origin ); + self doDOT_poisonDamage( trigger, damageMultiplier * 500 ); + break; + } + } +} + +onExitDOT_poisonDamagePlayer( idx, trigger ) +{ + AssertEx( IsDefined( idx ), "Must specify an index for this function" ); + AssertEx( IsDefined( trigger ), "trying to run tick function on DOT that has been removed or is undefined" ); + + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + overlays = self.onEnterDOT_poisonDamageOverlay; + + if ( IsDefined( overlays ) ) + { + foreach ( i, _ in overlays ) + { + if ( IsDefined( overlays[ i ] ) && + IsDefined( overlays[ i ][ entNum ] ) ) + { + overlays[ i ][ entNum ] thread doDOT_fadeOutBlackOut( 0.1, 0 ); + } + } + } + trigger notify( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); +} + +onDeathDOT_poisonDamagePlayer() +{ + entNum = self GetEntityNumber(); + + foreach ( player in level.players ) + { + overlays = player.onEnterDOT_poisonDamageOverlay; + + if ( IsDefined( overlays ) ) + { + foreach ( i, _ in overlays ) + { + if ( IsDefined( overlays[ i ] ) && + IsDefined( overlays[ i ][ entNum ] ) ) + { + overlays[ i ][ entNum ] thread doDOT_fadeOutBlackOutAndDestroy(); + } + } + } + } +} + +doDOT_poisonDamage( trigger, damage ) +{ + if ( isSP() ) + {/* + self doDamage( + damage, // iDamage Integer specifying the amount of damage done + self.origin, // vPoint The point the damage is from? + self, // eAttacker The entity that is attacking. + self // eInflictor The entity that causes the damage.( e.g. a turret ) + );*/ + } + else + { + self thread [[ level.callbackPlayerDamage ]]( + trigger, // eInflictor The entity that causes the damage.( e.g. a turret ) + trigger, // eAttacker The entity that is attacking. + damage, // iDamage Integer specifying the amount of damage done + 0, // iDFlags Integer specifying flags that are to be applied to the damage + "MOD_SUICIDE", // sMeansOfDeath Integer specifying the method of death + "claymore_mp", // sWeapon The weapon number of the weapon used to inflict the damage + trigger.origin, // vPoint The point the damage is from? + ( 0,0,0 ) - trigger.origin,// vDir The direction of the damage + "none", // sHitLoc The location of the hit + 0 // psOffsetTime The time offset for the damage + ); + } +} + +doDOT_poisonBlackout( idx, trigger ) +{ + AssertEx( IsDefined( idx ), "Must specify an index for this function" ); + AssertEx( IsDefined( trigger ), "trying to run tick function on DOT that has been removed or is undefined" ); + + entNum = trigger GetEntityNumber(); + playerNum = self GetEntityNumber(); + + trigger endon( "death" ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum ); + trigger endon( "LISTEN_kill_tick_" + idx + "_" + entNum + "_" + playerNum ); + + self endon( "disconnect" ); + self endon( "game_ended" ); + self endon( "death" ); + self endon( "LISTEN_exit_dot_" + entNum ); + + if ( !IsDefined( self.onEnterDOT_poisonDamageOverlay ) ) + self.onEnterDOT_poisonDamageOverlay = []; + if ( !IsDefined( self.onEnterDOT_poisonDamageOverlay[ idx ] ) ) + self.onEnterDOT_poisonDamageOverlay[ idx ] = []; + + if ( !IsDefined( self.onEnterDOT_poisonDamageOverlay[ idx ][ entNum ] ) ) + { + overlay = NewClientHudElem( self ); + overlay.x = 0; + overlay.y = 0; + overlay.alignX = "left"; + overlay.alignY = "top"; + overlay.horzAlign = "fullscreen"; + overlay.vertAlign = "fullscreen"; + overlay.alpha = 0; + overlay SetShader( "black", 640, 480 ); + + self.onEnterDOT_poisonDamageOverlay[ idx ][ entNum ] = overlay; + } + + overlay = self.onEnterDOT_poisonDamageOverlay[ idx ][ entNum ]; + + min_length = 1; + max_length = 2; + min_alpha = .25; + max_alpha = 1; + + min_percent = 5; + max_percent = 100; + + fraction = 0; + + for ( ;; ) + { + while ( self.onEnterDOT_poisonDamageCount[ idx ][ entNum ] > 1 ) + { + percent_range = max_percent - min_percent; + fraction = ( self.onEnterDOT_poisonDamageCount[ idx ][ entNum ] - min_percent ) / percent_range; + + if ( fraction < 0 ) + fraction = 0; + else if ( fraction > 1 ) + fraction = 1; + + length_range = max_length - min_length; + length = min_length + ( length_range * ( 1 - fraction ) ); + + alpha_range = max_alpha - min_alpha; + alpha = min_alpha + ( alpha_range * fraction ); + + end_alpha = fraction * 0.5; + + if ( fraction == 1 ) + break; + + duration = length / 2; + + overlay doDOT_fadeInBlackOut( duration, alpha ); + overlay doDOT_fadeOutBlackOut( duration, end_alpha ); + + wait( fraction * 0.5 ); + } + + if ( fraction == 1 ) + break; + + if ( overlay.alpha != 0 ) + overlay doDOT_fadeOutBlackOut( 1, 0 ); + + wait 0.05; + } + overlay doDOT_fadeInBlackOut( 2, 0 ); +} + +doDOT_fadeInBlackOut( duration, alpha ) +{ + self fadeOverTime( duration ); + self.alpha = alpha; + alpha = undefined; + wait duration; +} + +doDOT_fadeOutBlackOut( duration, alpha ) +{ + self fadeOverTime( duration ); + self.alpha = alpha; + alpha = undefined; + wait duration; +} + +doDOT_fadeOutBlackOutAndDestroy( duration, alpha ) +{ + self fadeOverTime( duration ); + self.alpha = alpha; + alpha = undefined; + wait duration; + self Destroy(); +} + +triggerTouchThink( enterFunc, exitFunc ) +{ + level endon( "game_ended" ); + self endon ( "death" ); + + self.entNum = self GetEntityNumber(); + + for ( ; ; ) + { + self waittill( "trigger", player ); + + if ( !isPlayer( player ) && !isDefined( player.finished_spawning ) ) + continue; + + if ( !isAlive( player ) ) + continue; + + if ( !isDefined( player.touchTriggers[ self.entNum ] ) ) + player thread playerTouchTriggerThink( self, enterFunc, exitFunc ); + } +} + +// This is modified ver of _dynamic_world::playerTouchTriggerThink + +playerTouchTriggerThink( trigger, enterFunc, exitFunc ) +{ + trigger endon( "death" ); + + if ( !isPlayer( self ) ) + self endon( "death" ); + + if ( !isSP() ) + touchName = self.guid; // generate GUID + else + touchName = "player" + gettime(); // generate GUID + + trigger.touchList[ touchName ] = self; + if ( isDefined( trigger.moveTracker ) ) + self.moveTrackers++ ; + + trigger notify( "trigger_enter", self ); + self notify( "trigger_enter", trigger ); + + // Check if player is already touching a trigger belonging to a DOT group + + doEnterExitFunc = true; + + foreach ( trig in trigger.DOT_group ) + foreach ( _trig in self.touchTriggers ) + if ( trig == _trig ) + doEnterExitFunc = false; + + if ( doEnterExitFunc && IsDefined( enterFunc ) ) + self thread [[ enterFunc ]]( trigger ); + + self.touchTriggers[ trigger.entNum ] = trigger; + + // Do some extra checks to see if the player is touching any other trigger in the same group + + while ( IsAlive( self ) && ( isSP() || !level.gameEnded ) ) + { + touchingTrigger = true; + + if ( self IsTouching( trigger ) ) + wait 0.05; + else + { + if ( !trigger.DOT_group.size ) + touchingTrigger = false; + + foreach ( trig in trigger.DOT_group ) + { + if ( self IsTouching( trig ) ) + { + wait 0.05; + break; + } + else + { + touchingTrigger = false; + } + } + } + + if ( !touchingTrigger ) + break; + } + + // disconnected player will skip this code + if ( isDefined( self ) ) + { + self.touchTriggers[ trigger.entNum ] = undefined; + if ( isDefined( trigger.moveTracker ) ) + self.moveTrackers -- ; + + self notify( "trigger_leave", trigger ); + + if ( doEnterExitFunc && IsDefined( exitFunc ) ) + self thread [[ exitFunc ]]( trigger ); + } + + if ( !isSP() && level.gameEnded ) + return; + + trigger.touchList[ touchName ] = undefined; + trigger notify( "trigger_leave", self ); + + if ( !anythingTouchingTrigger( trigger ) ) + trigger notify( "trigger_empty" ); +} + +anythingTouchingTrigger( trigger ) +{ + return( trigger.touchList.size ); +} + + +get_precached_anim( animname ) +{ + println( animname ); + assertEX( isdefined( level._destructible_preanims ) && isdefined( level._destructible_preanims[ animname ] ),"Can't find destructible anim: "+animname+" check the Build Precache Scripts and Repackage Zone boxes In launcher when you compile your map. " ); + return level._destructible_preanims[ animname ]; +} + +get_precached_animtree( animname ) +{ + println( animname ); + AssertEX( Isdefined( level._destructible_preanimtree ) && Isdefined( level._destructible_preanimtree[ animname ] ),"Can't find destructible anim tree: "+animname+" check the Build Precache Scripts and Repackage Zone boxes In launcher when you compile your map. " ); + return level._destructible_preanimtree[ animname ]; +} + +destructibleCoverWatcher() +{ + if( !IsDefined( level.player ) ) + return; + + if (!IsDefined(self.script_dest_cover_dmg_dist)) + self.script_dest_cover_dmg_dist = 20000; + + while(IsDefined(self)) + { + if(IsDefined(self.destructible_parts)) + { + chunkDestroyedCounter = 0; + for(i = 1; i < self.destructible_parts.size; i++) + { + if(self.destructible_parts[i].v["currentState"] == 1) + chunkDestroyedCounter++; + } + if(chunkDestroyedCounter == self.destructible_parts.size -1 ) + { + break; + } + } + + dist =DistanceSquared(level.player.origin, self.origin); + if(dist > self.script_dest_cover_dmg_dist * self.script_dest_cover_dmg_dist) + self SetCanDamage(false); + else + self SetCanDamage(true); + + wait(.05); + } +} + diff --git a/raw/common_scripts/_dynamic_world.gsc b/raw/common_scripts/_dynamic_world.gsc new file mode 100644 index 0000000..186d32d --- /dev/null +++ b/raw/common_scripts/_dynamic_world.gsc @@ -0,0 +1,1793 @@ +#include common_scripts\utility; + +/*QUAKED trigger_multiple_dyn_metal_detector (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_multiple_dyn_creaky_board (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_multiple_dyn_photo_copier (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_multiple_dyn_copier_no_light (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_radius_dyn_motion_light (0.12 0.23 1.0) (-16 -16 -16) (16 16 16) +Comments to be added.*/ + +/*QUAKED trigger_radius_dyn_motion_dlight (0.12 0.23 1.0) (-16 -16 -16) (16 16 16) +Comments to be added.*/ + +/*QUAKED trigger_multiple_dog_bark (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +Comments to be added.*/ + +/*QUAKED trigger_radius_bird_startle (0.12 0.23 1.0) (-16 -16 -16) (16 16 16) +Comments to be added.*/ + +/*QUAKED trigger_multiple_dyn_motion_light (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_multiple_dyn_door (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Comments to be added.*/ + +/*QUAKED trigger_multiple_freefall (0.12 0.23 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE +defaulttexture="flag" +Player free falling with animation and screaming of doom.*/ + +// Crouch Speed 5.7-6.0 +// Run Speed 8.7-9.2 +// Sprint Speed 13.0-14.0 + + +// ========================= Constants ========================== + +// Vending Machine +CONST_vending_machine_health = 400; +CONST_soda_pop_time = 0.2; //seconds +CONST_soda_count = 12; //number of soda per machine +CONST_soda_launch_force = 1000; //soda shoot out force +CONST_soda_random_factor = 0.3; //in percentage 0.2 = 20% +CONST_soda_splash_dmg_scaler = 3; //splash damage multiplier + +// Metal Detector +CONST_alarm_tolerance = 0; //number of alarm sounds before silenced, 0 disables silencing +CONST_alarm_interval = 7; //alarm interval time in seconds +CONST_alarm_interval_sp = 2; //alarm interval time in seconds for single player + +// Civilian Jet +CONST_jet_speed = 2000; //jet while landing is 130 - 160mph( 2292inch / sec - 2820inch / sec ), emergency landing is 110mph +CONST_jet_extend = 20000; //units, each jet and flyto origin will extend from each other by + +init() +{ + + //rotate fan blades in mp_highrise + array_thread( GetEntArray( "com_wall_fan_blade_rotate_slow", "targetname" ), ::fan_blade_rotate, "veryslow" ); + array_thread( GetEntArray( "com_wall_fan_blade_rotate", "targetname" ), ::fan_blade_rotate, "slow" ); + array_thread( GetEntArray( "com_wall_fan_blade_rotate_fast", "targetname" ), ::fan_blade_rotate, "fast" ); + + trigger_classes = []; + trigger_classes[ "trigger_multiple_dyn_metal_detector" ] = ::metal_detector; + trigger_classes[ "trigger_multiple_dyn_creaky_board" ] = ::creaky_board; + trigger_classes[ "trigger_multiple_dyn_photo_copier" ] = ::photo_copier; + trigger_classes[ "trigger_multiple_dyn_copier_no_light" ] = ::photo_copier_no_light; + trigger_classes[ "trigger_radius_motion_light" ] = ::motion_light; + trigger_classes[ "trigger_radius_dyn_motion_dlight" ] = ::outdoor_motion_dlight; + trigger_classes[ "trigger_multiple_dog_bark" ] = ::dog_bark; + trigger_classes[ "trigger_radius_bird_startle" ] = ::bird_startle; + trigger_classes[ "trigger_multiple_dyn_motion_light" ] = ::motion_light; + trigger_classes[ "trigger_multiple_dyn_door" ] = ::trigger_door; + //trigger_classes[ "trigger_multiple_freefall" ] = ::freefall; + + player_init(); + + foreach ( classname, function in trigger_classes ) + { + triggers = GetEntArray( classname, "classname" ); + // entities process + array_thread( triggers , ::triggerTouchThink ); + array_thread( triggers , function ); + } + + array_thread( GetEntArray( "vending_machine", "targetname" ), ::vending_machine ); + array_thread( GetEntArray( "toggle", "targetname" ), ::use_toggle ); + array_thread( getEntArray( "sliding_door", "targetname"), ::sliding_door ); + + level thread onPlayerConnect(); + + civilian_jet = GetEnt( "civilian_jet_origin", "targetname" ); + if ( IsDefined( civilian_jet ) ) + civilian_jet thread civilian_jet_flyby(); + + thread interactive_tv(); +} + +onPlayerConnect() +{ + for ( ;; ) + { + level waittill( "connecting", player ); + player thread movementTracker(); + } +} + +player_init() +{ + if ( isSP() ) + { + foreach ( player in level.players ) + { + player.touchTriggers = []; + player thread movementTracker(); + } + } +} + +ai_init() +{ + /*if ( !isdefined( level.registeredAI ) ) + level.registeredAI = []; + + level.registeredAI[ level.registeredAI.size ] = self; + */ + + // self is AI + self.touchTriggers = []; + self thread movementTracker(); +} + +// ================================================================================ // +// Civilian Jet // +// ================================================================================ // + +civilian_jet_flyby() +{ + level endon( "game_ended" ); + + self jet_init(); + + level waittill( "prematch_over" ); + + while ( 1 ) + { + self thread jet_timer(); + self waittill( "start_flyby" ); + + self thread jet_flyby(); + self waittill( "flyby_done" ); + + self jet_reset(); + } +} + +jet_init() +{ + // move jet plane and flyto origin out of the map and hide on level load + self.jet_parts = GetEntArray( self.target, "targetname" ); + self.jet_flyto = GetEnt( "civilian_jet_flyto", "targetname" ); + self.engine_fxs = GetEntArray( "engine_fx", "targetname" ); + self.flash_fxs = GetEntArray( "flash_fx", "targetname" ); + + // add these to your level csv + self.jet_engine_fx = LoadFX( "fx/fire/jet_afterburner" ); + self.jet_flash_fx_red = loadfx( "vfx/lights/aircraft_light_wingtip_red" ); + self.jet_flash_fx_green = loadfx( "vfx/lights/aircraft_light_wingtip_green" ); + self.jet_flash_fx_blink = loadfx( "vfx/lights/aircraft_light_red_blink" ); + + level.civilianJetFlyBy = undefined; // priority with air supremacies + + AssertEx( IsDefined( self.jet_parts ), "Missing cilivian jet model" ); + AssertEx( IsDefined( self.jet_flyto ), "Missing cilivian jet flyto script_origin: civilian_jet_flyto" ); + AssertEx( IsDefined( self.engine_fxs ), "Missing cilivian jet engine fxs script_origins: engine_fx" ); + AssertEx( IsDefined( self.flash_fxs ), "Missing cilivian jet signal light script_origins: flash_fxs" ); + + // extending vector to place jet and flyto origin outside sky box + negative_vec = ( VectorNormalize( self.origin - self.jet_flyto.origin ) * CONST_jet_extend ); + + // extend flyto origin + self.jet_flyto.origin -= negative_vec; + + // extend jet + self.origin += negative_vec; + foreach ( part in self.jet_parts ) + { + part.origin += negative_vec; + part.old_origin = part.origin; + part Hide(); + } + + // extend jet's engine fx origins + foreach ( engine_fx in self.engine_fxs ) + engine_fx.origin += negative_vec; + + foreach ( flash_fx in self.flash_fxs ) + flash_fx.origin += negative_vec; + + // -------------- flight time and vector calculation ------------- + jet_origin = self.origin; // origin is the nose of the jet + jet_flyto_pos = self.jet_flyto.origin; + self.jet_fly_vec = jet_flyto_pos - jet_origin; + + jet_speed = CONST_jet_speed; + jet_flight_dist = abs( Distance( jet_origin, jet_flyto_pos ) ); + self.jet_flight_time = jet_flight_dist / jet_speed; +} + +jet_reset() +{ + foreach ( part in self.jet_parts ) + { + part.origin = part.old_origin; + part Hide(); + } +} + +jet_timer() +{ + level endon( "game_ended" ); + + match_timelimit = getTimeInterval(); + Assert( IsDefined( match_timelimit ) ); + timelimit = max( 10 , match_timelimit ); + timelimit = min( timelimit, 100 ); + + if ( GetDvar( "jet_flyby_timer" ) != "" ) + level.civilianJetFlyBy_timer = 5 + GetDvarInt( "jet_flyby_timer" ); + else + level.civilianJetFlyBy_timer = ( 0.25 + RandomFloatRange( 0.3, 0.7 ) ) * 60 * timeLimit; // seconds into the match when jet flys by + + wait level.civilianJetFlyBy_timer; + + // wait till all the airborne kill streaks are done + while ( IsDefined( level.airstrikeInProgress ) || IsDefined( level.ac130player ) || IsDefined( level.chopper ) || IsDefined( level.remoteMissileInProgress ) ) + wait 0.05; + + // start flyby + self notify( "start_flyby" ); + + // blocks out all airborne kill streaks + level.civilianJetFlyBy = true; + self waittill( "flyby_done" ); + level.civilianJetFlyBy = undefined; +} + +getTimeInterval() +{ + if ( isSP() ) + return 10.0; + + if ( IsDefined( game[ "status" ] ) && game[ "status" ] == "overtime" ) + return 1.0; + else + return getWatchedDvar( "timelimit" ); +} + +getWatchedDvar( dvarString ) +{ + dvarString = "scr_" + level.gameType + "_" + dvarString; + + if ( IsDefined( level.overrideWatchDvars ) && IsDefined( level.overrideWatchDvars[ dvarString ] ) ) + { + return level.overrideWatchDvars[ dvarString ]; + } + + return( level.watchDvars[ dvarString ].value ); +} + +jet_flyby() +{ + // show plane + foreach ( part in self.jet_parts ) + part Show(); + + engine_fx_array = []; + flash_fx_array = []; + + foreach ( engine_fx in self.engine_fxs ) + { + engine_fx_ent = Spawn( "script_model", engine_fx.origin ); + engine_fx_ent SetModel( "tag_origin" ); + engine_fx_ent.angles = engine_fx.angles; + engine_fx_array[ engine_fx_array.size ] = engine_fx_ent; + } + + foreach ( flash_fx in self.flash_fxs ) + { + flash_fx_ent = Spawn( "script_model", flash_fx.origin ); + flash_fx_ent SetModel( "tag_origin" ); + flash_fx_ent.color = flash_fx.script_noteworthy; + flash_fx_ent.angles = flash_fx.angles; + flash_fx_array[ flash_fx_array.size ] = flash_fx_ent; + } + + AssertEx( IsDefined( level.mapcenter ), "Calling for civilian jet flyby when level.mapcenter is not yet defined." ); + self thread jet_planeSound( self.jet_parts[ 0 ], level.mapcenter ); + + wait 0.05; + + // play engine fx on fx ents + foreach ( engine_fx_ent in engine_fx_array ) + PlayFXOnTag( self.jet_engine_fx, engine_fx_ent, "tag_origin" ); + + // play flash fx on fx ents + foreach ( flash_fx_ent in flash_fx_array ) + { + if ( IsDefined( flash_fx_ent.color ) && flash_fx_ent.color == "blink" ) + PlayFXOnTag( self.jet_flash_fx_blink, flash_fx_ent, "tag_origin" ); + else if ( IsDefined( flash_fx_ent.color ) && flash_fx_ent.color == "red" ) + PlayFXOnTag( self.jet_flash_fx_red, flash_fx_ent, "tag_origin" ); + else + PlayFXOnTag( self.jet_flash_fx_green, flash_fx_ent, "tag_origin" ); + } + + // move plane + foreach ( part in self.jet_parts ) + part MoveTo( part.origin + self.jet_fly_vec, self.jet_flight_time ); + + // move fx ents + foreach ( engine_fx_ent in engine_fx_array ) + engine_fx_ent MoveTo( engine_fx_ent.origin + self.jet_fly_vec, self.jet_flight_time ); + foreach ( flash_fx_ent in flash_fx_array ) + flash_fx_ent MoveTo( flash_fx_ent.origin + self.jet_fly_vec, self.jet_flight_time ); + + wait( self.jet_flight_time + 1 ); + + // delete fxs + foreach ( engine_fx_ent in engine_fx_array ) + engine_fx_ent Delete(); + foreach ( flash_fx_ent in flash_fx_array ) + flash_fx_ent Delete(); + + self notify( "flyby_done" ); +} + +jet_planeSound( plane, bombsite ) +{ + plane thread playsound_loop_on_ent( "veh_mig29_dist_loop" ); + while ( !targetisclose( plane, bombsite ) ) + wait 0.05; + + plane thread playsound_loop_on_ent( "veh_mig29_close_loop" ); + while ( targetisinfront( plane, bombsite ) ) + wait 0.05; + wait 0.5; + + plane thread playsound_float( "veh_mig29_sonic_boom" ); + while ( targetisclose( plane, bombsite ) ) + wait 0.05; + + plane notify( "stop sound" + "veh_mig29_close_loop" ); + self waittill( "flyby_done" ); + + plane notify( "stop sound" + "veh_mig29_dist_loop" ); +} + +playsound_float( alias, origin, master ) +{ + org = Spawn( "script_origin", ( 0, 0, 1 ) ); + org Hide(); + if ( !IsDefined( origin ) ) + origin = self.origin; + org.origin = origin; + if ( IsDefined( master ) && master ) + org PlaySoundAsMaster( alias ); + else + org PlaySound( alias ); + wait( 10.0 ); + org Delete(); +} + +playsound_loop_on_ent( alias, offset ) +{ + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + org Hide(); + org endon( "death" ); + thread delete_on_death( org ); + if ( IsDefined( offset ) ) + { + org.origin = self.origin + offset; + org.angles = self.angles; + org LinkTo( self ); + } + else + { + org.origin = self.origin; + org.angles = self.angles; + org LinkTo( self ); + } +// org endon ("death"); + org PlayLoopSound( alias ); +// println ("playing loop sound ", alias," on entity at origin ", self.origin, " at ORIGIN ", org.origin); + self waittill( "stop sound" + alias ); + org StopLoopSound( alias ); + org Delete(); +} + +targetisinfront( other, target ) +{ + forwardvec = AnglesToForward( flat_angle( other.angles ) ); + normalvec = VectorNormalize( flat_origin( target ) - other.origin ); + dot = VectorDot( forwardvec, normalvec ); + + if ( dot > 0 ) + return true; + else + return false; +} + +targetisclose( other, target ) +{ + infront = targetisinfront( other, target ); + + if ( infront ) + dir = 1; + else + dir = -1; + + a = flat_origin( other.origin ); + b = a + ( AnglesToForward( flat_angle( other.angles ) ) * ( dir * 100000 ) ); + point = PointOnSegmentNearestToPoint( a, b, target ); + dist = Distance( a, point ); + + if ( dist < 3000 ) + return true; + else + return false; +} + +// ================================================================================ // +// Vending Machine // +// ================================================================================ // + +vending_machine() +{ + level endon( "game_ended" ); + self endon( "death" ); + + // self is use trigger + self SetCursorHint( "HINT_ACTIVATE" ); + + self.vm_normal = GetEnt( self.target, "targetname" ); + AssertEx( IsDefined( self.vm_normal ), "Vending machine use trigger is missing target to the normal vending machine script_model" ); + vm_soda_start = GetEnt( self.vm_normal.target, "targetname" ); + AssertEx( IsDefined( vm_soda_start ), "Vending machine normal script_model is missing target to the start-soda can script_model" ); + vm_soda_stop = GetEnt( vm_soda_start.target, "targetname" ); + AssertEx( IsDefined( vm_soda_start ), "Start-soda can script_model is missing target to the end-soda can script_model" ); + vm_launch_from = GetEnt( vm_soda_stop.target, "targetname" ); + AssertEx( IsDefined( vm_launch_from ), "End-soda can script_model is missing target to the physics launch-from script_origin" ); + self.vm_launch_from = vm_launch_from.origin; + vm_launch_to = GetEnt( vm_launch_from.target, "targetname" ); + AssertEx( IsDefined( vm_launch_to ), "launch-from can script_origin is missing target to the physics launch-to script_origin" ); + self.vm_launch_to = vm_launch_to.origin; + + if ( IsDefined( vm_launch_to.target ) ) + self.vm_fx_loc = GetEnt( vm_launch_to.target, "targetname" ).origin; + + //assertex( isdefined( self.vm_launch_to ), "launch-to can script_origin is missing target to the fx location script_origin" ); + + self.vm_normal SetCanDamage( true ); + + self.vm_normal_model = self.vm_normal.model; + self.vm_damaged_model = self.vm_normal.script_noteworthy; + self.vm_soda_model = vm_soda_start.model; + + self.vm_soda_start_pos = vm_soda_start.origin; + self.vm_soda_start_angle = vm_soda_start.angles; + self.vm_soda_stop_pos = vm_soda_stop.origin; + self.vm_soda_stop_angle = vm_soda_stop.angles; + + // precache damage model + PreCacheModel( self.vm_damaged_model ); + + // ride the no longer needed models + vm_soda_start Delete(); + vm_soda_stop Delete(); + vm_launch_from Delete(); + vm_launch_to Delete(); + + self.soda_array = []; + self.soda_count = CONST_soda_count; + self.soda_slot = undefined; // the soda can thats resting in the slot + self.hp = CONST_vending_machine_health; + + self thread vending_machine_damage_monitor( self.vm_normal ); + self PlayLoopSound( "vending_machine_hum" ); + + while ( 1 ) + { + self waittill( "trigger", player ); + //level.players[0] iprintln( "used" ); + + self PlaySound( "vending_machine_button_press" ); + if ( !self.soda_count ) + continue; + + // drop a can, and shoot out the previous one if in slot + if ( IsDefined( self.soda_slot ) ) + self soda_can_eject(); + soda_can_drop( spawn_soda() ); + wait 0.05; + } +} + +vending_machine_damage_monitor( vending_machine ) +{ + level endon( "game_ended" ); + + exp_dmg = "mod_grenade mod_projectile mod_explosive mod_grenade_splash mod_projectile_splash splash"; + sparks_fx = LoadFX( "fx/explosions/tv_explosion" ); + + while ( 1 ) + { + damage = undefined; + other = undefined; + direction_vec = undefined; + P = undefined; + type = undefined; + vending_machine waittill( "damage", damage, other, direction_vec, P, type ); + + if ( IsDefined( type ) ) + { + if ( IsSubStr( exp_dmg, ToLower( type ) ) ) + damage *= CONST_soda_splash_dmg_scaler; // multiply explosive dmg + + self.hp -= damage; + if ( self.hp > 0 ) + continue; + + // vending machine is now dead, button usage is disabled + self notify( "death" ); + + // disable use trigger + self.origin += ( 0, 0, 10000 ); + + if ( !IsDefined( self.vm_fx_loc ) ) + playfx_loc = self.vm_normal.origin + ( ( 17, -13, 52 ) - ( -20, 18, 0 ) ); + else + playfx_loc = self.vm_fx_loc; + + PlayFX( sparks_fx, playfx_loc ); + + // when vending machine is explosively damaged, shoots out soda cans + self.vm_normal SetModel( self.vm_damaged_model ); + + while ( self.soda_count > 0 ) + { + // drop a can, and shoot out the previous one if in slot + if ( IsDefined( self.soda_slot ) ) + self soda_can_eject(); + soda_can_drop( spawn_soda() ); + wait 0.05; + } + + self StopLoopSound( "vending_machine_hum" ); + return; + } + } +} + +spawn_soda() +{ + soda = Spawn( "script_model", self.vm_soda_start_pos ); + soda SetModel( self.vm_soda_model ); + soda.origin = self.vm_soda_start_pos; + soda.angles = self.vm_soda_start_angle; + return soda; +} + +soda_can_drop( soda ) +{ + soda MoveTo( self.vm_soda_stop_pos, CONST_soda_pop_time ); + soda PlaySound( "vending_machine_soda_drop" ); // soda can drop sound + wait CONST_soda_pop_time; + + self.soda_slot = soda; + self.soda_count--; +} + +soda_can_eject() +{ + self endon( "death" ); + + if ( IsDefined( self.soda_slot.ejected ) && self.soda_slot.ejected == true ) + return; + + // physics launch + force_max = 1; + force_min = force_max * ( 1 - CONST_soda_launch_force ); + + random_offset = Int( 40 * CONST_soda_launch_force ); + random_launch_offset = ( Int( random_offset / 2 ), Int( random_offset / 2 ), 0 ) - ( RandomInt( random_offset ), RandomInt( random_offset ), 0 ); + + launch_vec = VectorNormalize( self.vm_launch_to - self.vm_launch_from + random_launch_offset ); + launch_force_vec = ( launch_vec * RandomFloatRange( force_min, force_max ) ); + + self.soda_slot PhysicsLaunchClient( self.vm_launch_from, launch_force_vec ); + self.soda_slot.ejected = true; +} + +// ================================================================================ // +// Free Fall // +// ================================================================================ // + +freefall() +{ + level endon( "game_ended" ); + + freefall_weapon = "briefcase_bomb_mp"; + PreCacheItem( freefall_weapon ); + + while ( 1 ) + { + self waittill( "trigger_enter", player ); + + if ( !( player HasWeapon( freefall_weapon ) ) ) + { + player PlaySound( "freefall_death" ); + + player GiveWeapon( freefall_weapon ); + player SetWeaponAmmoStock( freefall_weapon, 0 ); + player SetWeaponAmmoClip( freefall_weapon, 0 ); + player SwitchToWeapon( freefall_weapon ); + } + } +} + +// ================================================================================ // +// Metal Detector // +// ================================================================================ // + +metal_detector() +{ + // self is trigger: trigger_multiple_dyn_metal_detector + + level endon( "game_ended" ); + AssertEx( IsDefined( self.target ), "trigger_multiple_dyn_metal_detector is missing target damage trigger used for detecting entities other than players" ); + + damage_trig = GetEnt( self.target, "targetname" ); + damage_trig EnableGrenadeTouchDamage(); + + bound_org_1 = GetEnt( damage_trig.target, "targetname" ); + bound_org_2 = GetEnt( bound_org_1.target, "targetname" ); + + AssertEx( IsDefined( bound_org_1 ) && IsDefined( bound_org_2 ), "Metal detector missing bound origins for claymore test" ); + + detector_1 = GetEnt( bound_org_2.target, "targetname" ); + detector_2 = GetEnt( detector_1.target, "targetname" ); + + AssertEx( IsDefined( detector_1 ) && IsDefined( detector_2 ), "Recompile the bsp to fix this, metal detector prefab changed." ); + + bounds = []; + bound_x_min = min( bound_org_1.origin[ 0 ], bound_org_2.origin[ 0 ] ); bounds[ 0 ] = bound_x_min; + bound_x_max = max( bound_org_1.origin[ 0 ], bound_org_2.origin[ 0 ] ); bounds[ 1 ] = bound_x_max; + bound_y_min = min( bound_org_1.origin[ 1 ], bound_org_2.origin[ 1 ] ); bounds[ 2 ] = bound_y_min; + bound_y_max = max( bound_org_1.origin[ 1 ], bound_org_2.origin[ 1 ] ); bounds[ 3 ] = bound_y_max; + bound_z_min = min( bound_org_1.origin[ 2 ], bound_org_2.origin[ 2 ] ); bounds[ 4 ] = bound_z_min; + bound_z_max = max( bound_org_1.origin[ 2 ], bound_org_2.origin[ 2 ] ); bounds[ 5 ] = bound_z_max; + + bound_org_1 Delete(); + bound_org_2 Delete(); + + if ( !isSP() ) + self.alarm_interval = CONST_alarm_interval; + else + self.alarm_interval = CONST_alarm_interval_sp; + + self.alarm_playing = 0; + self.alarm_annoyance = 0; + self.tolerance = CONST_alarm_tolerance; + + self thread metal_detector_dmg_monitor( damage_trig ); + self thread metal_detector_touch_monitor(); + self thread metal_detector_weapons( bounds, "weapon_claymore", "weapon_c4" ); + + light_pos1 = ( detector_1.origin[ 0 ], detector_1.origin[ 1 ], bound_z_max ); + light_pos2 = ( detector_2.origin[ 0 ], detector_2.origin[ 1 ], bound_z_max ); + + //light_pos1 = ( bound_x_min, bound_y_min, bound_z_max ); + //light_pos2 = ( bound_x_max, bound_y_max, bound_z_max ); + md_light = LoadFX( "fx/props/metal_detector_light" ); + + while ( 1 ) + { + self waittill_any( "dmg_triggered", "touch_triggered", "weapon_triggered" ); + self thread playsound_and_light( "alarm_metal_detector", md_light, light_pos1, light_pos2 ); + } +} + +playsound_and_light( sound, light, light_pos1, light_pos2 ) +{ + level endon( "game_ended" ); + + if ( !self.alarm_playing ) + { + self.alarm_playing = 1; + self thread annoyance_tracker(); + + if ( !self.alarm_annoyance ) + self PlaySound( sound ); + + // 1000ms red light fx + PlayFX( light, light_pos1 ); + PlayFX( light, light_pos2 ); + + wait self.alarm_interval; + self.alarm_playing = 0; + } +} + +annoyance_tracker() +{ + level endon( "game_ended" ); + + if ( !self.tolerance ) + return; + + interval = self.alarm_interval + 0.15; + if ( self.tolerance ) + self.tolerance--; + else + self.alarm_annoyance = 1; + + current_time = GetTime(); // ms + + alarm_timeout = CONST_alarm_interval; + if ( isSP() ) + alarm_timeout = CONST_alarm_interval_sp; + + self waittill_any_or_timeout( "dmg_triggered", "touch_triggered", "weapon_triggered", ( alarm_timeout + 2 ) ); + + time_delta = ( GetTime() - current_time ); + if ( time_delta > ( ( alarm_timeout * 1000 ) + 1150 ) ) + { + self.alarm_annoyance = 0; + self.tolerance = CONST_alarm_tolerance; + } +} + +waittill_any_or_timeout( msg1, msg2, msg3, timer ) +{ + level endon( "game_ended" ); + + self endon( msg1 ); + self endon( msg2 ); + self endon( msg3 ); + wait timer; +} + +metal_detector_weapons( bounds, weapon_1, weapon_2 ) +{ + level endon( "game_ended" ); + while ( 1 ) + { + self waittill_weapon_placed(); + + all_grenades = GetEntArray( "grenade", "classname" ); + foreach ( grenade in all_grenades ) + { + if ( IsDefined( grenade.model ) && ( grenade.model == weapon_1 || grenade.model == weapon_2 ) ) + { + if ( isInBound( grenade, bounds ) ) + self thread weapon_notify_loop( grenade, bounds ); + } + } + } +} + +waittill_weapon_placed() +{ + level endon( "game_ended" ); + self endon( "dmg_triggered" ); + self waittill( "touch_triggered" ); +} + +weapon_notify_loop( grenade, bounds ) +{ + grenade endon( "death" ); + + while ( isInBound( grenade, bounds ) ) + { + self notify( "weapon_triggered" ); + wait self.alarm_interval; + } +} + +isInBound( ent, bounds ) +{ + bound_x_min = bounds[ 0 ]; bound_x_max = bounds[ 1 ]; + bound_y_min = bounds[ 2 ]; bound_y_max = bounds[ 3 ]; + bound_z_min = bounds[ 4 ]; bound_z_max = bounds[ 5 ]; + + ent_x = ent.origin[ 0 ]; + ent_y = ent.origin[ 1 ]; + ent_z = ent.origin[ 2 ]; + + if ( isInBound_single( ent_x, bound_x_min, bound_x_max ) ) + { + if ( isInBound_single( ent_y, bound_y_min, bound_y_max ) ) + { + if ( isInBound_single( ent_z, bound_z_min, bound_z_max ) ) + return true; + } + } + return false; +} + +isInBound_single( var, v_min, v_max ) +{ + if ( var > v_min && var < v_max ) + return true; + return false; +} + +metal_detector_dmg_monitor( damage_trig ) +{ + level endon( "game_ended" ); + while ( 1 ) + { + damage_trig waittill( "damage", damage, other, direction_vec, P, type ); + if ( IsDefined( type ) && alarm_validate_damage( type ) ) + self notify( "dmg_triggered" ); + } +} + +metal_detector_touch_monitor() +{ + level endon( "game_ended" ); + while ( 1 ) + { + self waittill( "trigger_enter" ); + while ( anythingTouchingTrigger( self ) ) + { + self notify( "touch_triggered" ); + wait self.alarm_interval; + } + } +} + +alarm_validate_damage( damageType ) +{ + //disallowed_dmg = "mod_pistol_bullet mod_rifle_bullet bullet mod_crush mod_grenade_splash mod_projectile_splash splash unknown"; + //disallowed_dmg_array = strtok( disallowed_damage, " " ); + + allowed_dmg = "mod_melee melee mod_grenade mod_projectile mod_explosive mod_impact"; + allowed_dmg_array = StrTok( allowed_dmg, " " ); + + foreach ( dmg in allowed_dmg_array ) + { + if ( ToLower( dmg ) == ToLower( damageType ) ) + return true; + } + return false; +} + +// ================================================================================ // + + +creaky_board() +{ + level endon( "game_ended" ); + + for ( ;; ) + { + self waittill( "trigger_enter", player ); + player thread do_creak( self ); + } +} + +do_creak( trigger ) +{ + self endon( "disconnect" ); + self endon( "death" ); + + self PlaySound( "step_walk_plr_woodcreak_on" ); + + for ( ;; ) + { + self waittill( "trigger_leave", leftTrigger ); + if ( trigger != leftTrigger ) + continue; + + self PlaySound( "step_walk_plr_woodcreak_off" ); + return; + } +} + +motion_light() +{ + level endon( "game_ended" ); + self.moveTracker = true; + + self.lightsOn = false; + lights = GetEntArray( self.target, "targetname" ); + AssertEx( lights.size, "ERROR: trigger_ * _motion_light with no targets at " + self.origin ); + + noself_array_call( [ "com_two_light_fixture_off", "com_two_light_fixture_on" ], ::PreCacheModel ); + + foreach ( light in lights ) + { + light.lightRigs = []; + infoNull = GetEnt( light.target, "targetname" ); + if ( !IsDefined( infoNull.target ) ) + continue; + + light.lightRigs = GetEntArray( infoNull.target, "targetname" ); + } + + + for ( ;; ) + { + self waittill( "trigger_enter" ); + + while ( anythingTouchingTrigger( self ) ) + { + objectMoved = false; + foreach ( object in self.touchList ) + { + if ( IsDefined( object.distMoved ) && object.distMoved > 5.0 ) + objectMoved = true; + } + + if ( objectMoved ) + { + if ( !self.lightsOn ) + { + self.lightsOn = true; + lights[ 0 ] PlaySound( "switch_auto_lights_on" ); + + foreach ( light in lights ) + { + light SetLightIntensity( 1.0 ); + + if ( IsDefined( light.lightRigs ) ) + { + foreach ( rig in light.lightRigs ) + rig SetModel( "com_two_light_fixture_on" ); + } + } + } + self thread motion_light_timeout( lights, 10.0 ); + } + + wait( 0.05 ); + } + } +} + +motion_light_timeout( lights, timeout ) +{ + self notify( "motion_light_timeout" ); + self endon( "motion_light_timeout" ); + + wait( timeout ); + + foreach ( light in lights ) + { + light SetLightIntensity( 0 ); + if ( IsDefined( light.lightRigs ) ) + { + foreach ( rig in light.lightRigs ) + rig SetModel( "com_two_light_fixture_off" ); + } + } + + lights[ 0 ] PlaySound( "switch_auto_lights_off" ); + + self.lightsOn = false; +} + +outdoor_motion_dlight() +{ + if ( !IsDefined( level.outdoor_motion_light ) ) + level.outdoor_motion_light = loadFx( "vfx/lights/outdoor_motion_light" ); + + level endon( "game_ended" ); + self.moveTracker = true; + + self.lightsOn = false; + lightRig = GetEnt( self.target, "targetname" ); + AssertEx( lightRig.size, "ERROR: trigger_ * _motion_light with no targets at " + self.origin ); + lights = GetEntArray( lightRig.target, "targetname" ); + AssertEx( lights.size, "ERROR: trigger_ * _motion_light model target with no light targets at " + lightRig.origin ); + noself_array_call( [ "com_two_light_fixture_off", "com_two_light_fixture_on" ], ::PreCacheModel ); + for ( ;; ) + { + self waittill( "trigger_enter" ); + + while ( anythingTouchingTrigger( self ) ) + { + objectMoved = false; + foreach ( object in self.touchList ) + { + if ( IsDefined( object.distMoved ) && object.distMoved > 5.0 ) + objectMoved = true; + } + + if ( objectMoved ) + { + if ( !self.lightsOn ) + { + self.lightsOn = true; + lightRig PlaySound( "switch_auto_lights_on" ); + lightRig SetModel( "com_two_light_fixture_on" ); + + foreach ( light in lights ) + { + Assert( !IsDefined( light.lightEnt ) ); + light.lightEnt = Spawn( "script_model", light.origin ); + light.lightEnt SetModel( "tag_origin" ); + PlayFXOnTag( level.outdoor_motion_light, light.lightEnt, "tag_origin" ); + } + } + self thread outdoor_motion_dlight_timeout( lightRig, lights, 10.0 ); + } + + wait( 0.05 ); + } + } +} + +outdoor_motion_dlight_timeout( lightRig, lights, timeout ) +{ + self notify( "motion_light_timeout" ); + self endon( "motion_light_timeout" ); + + wait( timeout ); + + foreach ( light in lights ) + { + Assert( IsDefined( light.lightEnt ) ); + light.lightEnt Delete(); + } + + lightRig PlaySound( "switch_auto_lights_off" ); + lightRig SetModel( "com_two_light_fixture_off" ); + + self.lightsOn = false; +} + +dog_bark() +{ + level endon( "game_ended" ); + self.moveTracker = true; + + dogOrigin = GetEnt( self.target, "targetname" ); + AssertEx( IsDefined( dogOrigin ), "ERROR: trigger_multiple_dog_bark with no target at " + self.origin ); + + for ( ;; ) + { + self waittill( "trigger_enter", player ); + + while ( anythingTouchingTrigger( self ) ) + { + maxDistMoved = 0; + foreach ( object in self.touchList ) + { + if ( IsDefined( object.distMoved ) && object.distMoved > maxDistMoved ) + maxDistMoved = object.distMoved; + } + + if ( maxDistMoved > 6.0 ) + { + dogOrigin PlaySound( "dyn_anml_dog_bark" ); + wait( RandomFloatRange( 16 / maxDistMoved, 16 / maxDistMoved + RandomFloat( 1.0 ) ) ); + } + else + { + wait( 0.05 ); + } + } + } +} + +trigger_door() +{ + doorEnt = GetEnt( self.target, "targetname" ); + AssertEx( IsDefined( doorEnt ), "ERROR: trigger_multiple_dyn_door with no door brush at " + self.origin ); + + self.doorEnt = doorEnt; + self.doorAngle = getVectorRightAngle( VectorNormalize( self GetOrigin() - doorEnt GetOrigin() ) ); + doorEnt.baseYaw = doorEnt.angles[ 1 ]; + openTime = 1.0; + + for ( ;; ) + { + self waittill( "trigger_enter", player ); + + doorEnt thread doorOpen( openTime, self getDoorSide( player ) ); + + if ( anythingTouchingTrigger( self ) ) + self waittill( "trigger_empty" ); + + wait( 3.0 ); + + if ( anythingTouchingTrigger( self ) ) + self waittill( "trigger_empty" ); + + doorEnt thread doorClose( openTime ); + } +} + +doorOpen( openTime, doorSide ) +{ + if ( doorSide ) + self RotateTo( ( 0, self.baseYaw + 90, 1 ), openTime, 0.1, 0.75 ); + else + self RotateTo( ( 0, self.baseYaw - 90, 1 ), openTime, 0.1, 0.75 ); + + self PlaySound( "door_generic_house_open" ); + + wait( openTime + 0.05 ); +} + +doorClose( openTime ) +{ + self RotateTo( ( 0, self.baseYaw, 1 ), openTime ); + self PlaySound( "door_generic_house_close" ); + + wait( openTime + 0.05 ); +} + +getDoorSide( player ) +{ + return( VectorDot( self.doorAngle, VectorNormalize( player.origin - self.doorEnt GetOrigin() ) ) > 0 ); +} + +getVectorRightAngle( vDir ) +{ + return( vDir[ 1 ], 0 - vDir[ 0 ], vDir[ 2 ] ); +} + +use_toggle() +{ + if ( self.classname != "trigger_use_touch" ) + return; + + lights = GetEntArray( self.target, "targetname" ); + Assert( lights.size ); + + self.lightsOn = 1; + foreach ( light in lights ) + light SetLightIntensity( 1.5 * self.lightsOn ); + + + for ( ;; ) + { + self waittill( "trigger" ); + + self.lightsOn = !self.lightsOn; + if ( self.lightsOn ) + { + foreach ( light in lights ) + light SetLightIntensity( 1.5 ); + + self PlaySound( "switch_auto_lights_on" ); + } + else + { + foreach ( light in lights ) + light SetLightIntensity( 0 ); + + self PlaySound( "switch_auto_lights_off" ); + } + } +} + +bird_startle() +{ +} + +photo_copier_init( trigger ) +{ + // self is trigger + + self.copier = get_photo_copier( trigger ); + if (IsDefined( self.copier ) ) + { + AssertEx( self.copier.classname == "script_model", "Photocopier at " + trigger.origin + " doesn't target a photo copier" ); + + copy_bar = GetEnt( self.copier.target, "targetname" ); + if (IsDefined(copy_bar)) + { + AssertEx( copy_bar.classname == "script_brushmodel", "Photocopier at " + trigger.origin + " doesn't target a photo copier" ); + + light = GetEnt( copy_bar.target, "targetname" ); + if (IsDefined( light ) ) + { + AssertEx( light.classname == "light_spot" || light.classname == "light", "Photocopier at " + trigger.origin + " doesn't have a light" ); + + light.intensity = light GetLightIntensity(); + light SetLightIntensity( 0 ); + trigger.copy_bar = copy_bar; + trigger.start_pos = copy_bar.origin; + trigger.light = light; + + angles = self.copier.angles + ( 0, 90, 0 ); + forward = AnglesToForward( angles ); + trigger.end_pos = trigger.start_pos + ( forward * 30 ); + } + } + } +} + +get_photo_copier( trigger ) +{ + if ( !IsDefined( trigger.target ) ) + { + //cant target directly to a destructible toy, so we are grabing the nearest one, since primary light requires them to be far anyway + toys = GetEntArray( "destructible_toy", "targetname" ); + copier = toys[ 0 ]; + foreach ( toy in toys ) + { + if ( IsDefined( toy.destructible_type ) && toy.destructible_type == "toy_copier" ) + { + if ( Distance( trigger.origin, copier.origin ) > Distance( trigger.origin, toy.origin ) ) + copier = toy; + } + } + AssertEx( Distance( trigger.origin, copier.origin ) < 128, "Photocopier at " + trigger.origin + " doesn't contain a photo copier" ); + } + else + { + copier = GetEnt( trigger.target, "targetname" ); + if (IsDefined(copier)) + { + AssertEx( IsDefined( copier ), "Photocopier at " + trigger.origin + " doesn't target a photo copier" ); + copier SetCanDamage( true ); + } + } + + return copier; +} + +waittill_copier_copies() +{ + if (!IsDefined( self.copier ) ) + return; + + self.copier endon( "FX_State_Change0" ); + self.copier endon( "death" ); + + self waittill( "trigger_enter" ); +} + +photo_copier() +{ + level endon( "game_ended" ); + photo_copier_init( self ); + + if (!IsDefined( self.copier ) ) + return; + + self.copier endon( "FX_State_Change0" ); // this is when copier breaks + self thread photo_copier_stop(); // monitor copier for quick stop + + for ( ;; ) + { + waittill_copier_copies(); + + self PlaySound( "mach_copier_run" ); + + if ( IsDefined( self.copy_bar ) ) + { + reset_copier( self ); + thread photo_copier_copy_bar_goes(); + thread photo_copier_light_on(); + } + wait( 3 ); + } +} + +photo_copier_no_light() +{ + level endon( "game_ended" ); + self endon ( "death" ); + + if ( get_template_level() == "hamburg" ) + return; // I don't need no stinking copies. // masking is not friendly for this - Nate + + self.copier = get_photo_copier( self ); + + if (!IsDefined( self.copier ) ) + return; + + AssertEx( self.copier.classname == "script_model", "Photocopier at " + self.origin + " doesn't target or contain a photo copier" ); + + self.copier endon( "FX_State_Change0" ); // this is when copier breaks + + for ( ;; ) + { + waittill_copier_copies(); + self PlaySound( "mach_copier_run" ); + wait( 3 ); + } +} + +// reset light and copy bar position, interruptes previous copy in progress +reset_copier( trigger ) +{ + trigger.copy_bar MoveTo( trigger.start_pos, 0.2 ); // reset position + trigger.light SetLightIntensity( 0 ); +} + +photo_copier_copy_bar_goes() +{ + self.copier notify( "bar_goes" ); + self.copier endon( "bar_goes" ); + self.copier endon( "FX_State_Change0" ); + self.copier endon( "death" ); + + copy_bar = self.copy_bar; + wait( 2.0 ); + copy_bar MoveTo( self.end_pos, 1.6 ); + wait( 1.8 ); + copy_bar MoveTo( self.start_pos, 1.6 ); + wait( 1.6 ); // wait( 13.35 ); + + light = self.light; + timer = 0.2; + steps = timer / 0.05; + + for ( i = 0; i < steps; i++ ) + { + intensity = i * 0.05; + intensity /= timer; + intensity = 1 - ( intensity * light.intensity ); + if ( intensity > 0 ) + light SetLightIntensity( intensity ); + wait( 0.05 ); + } +} + +photo_copier_light_on() +{ + self.copier notify( "light_on" ); + self.copier endon( "light_on" ); + self.copier endon( "FX_State_Change0" ); + self.copier endon( "death" ); + + light = self.light; + timer = 0.2; + steps = timer / 0.05; + + for ( i = 0; i < steps; i++ ) + { + intensity = i * 0.05; + intensity /= timer; + light SetLightIntensity( intensity * light.intensity ); + wait( 0.05 ); + } + + photo_light_flicker( light ); +} + +// stopping light and bar move on death +photo_copier_stop() +{ + self.copier waittill( "FX_State_Change0" ); + self.copier endon( "death" ); + + reset_copier( self ); +} + +photo_light_flicker( light ) +{ + // flicker + light SetLightIntensity( 1 ); + wait( 0.05 ); + light SetLightIntensity( 0 ); + wait( 0.10 ); + light SetLightIntensity( 1 ); + wait( 0.05 ); + light SetLightIntensity( 0 ); + wait( 0.10 ); + light SetLightIntensity( 1 ); +} + +fan_blade_rotate( type ) +{ + Assert( IsDefined( type ) ); + + speed = 0; + time = 20000; + + speed_multiplier = 1.0; + if ( IsDefined( self.speed ) ) + { + speed_multiplier = self.speed; + } + + if ( type == "slow" ) + { + if ( IsDefined( self.script_noteworthy ) && ( self.script_noteworthy == "lockedspeed" ) ) + speed = 180; + else + speed = RandomFloatRange( 100 * speed_multiplier, 360 * speed_multiplier ); + } + else if ( type == "fast" ) + speed = RandomFloatRange( 720 * speed_multiplier, 1000 * speed_multiplier ); + else if ( type == "veryslow" ) + speed = RandomFloatRange( 1 * speed_multiplier, 2 * speed_multiplier ); // use the speed to really tune + else + AssertMsg( "Type must be fast, slow, or veryslow" ); + + if ( IsDefined( self.script_noteworthy ) && ( self.script_noteworthy == "lockedspeed" ) ) + wait 0; + else + wait RandomFloatRange( 0, 1 ); + + if( !isDefined( self ) )//adding this for game modes where we remove objects (example: the fan blades in mp_laser2 for atdm) + return; + + fan_angles = self.angles; + fan_vec = ( AnglesToRight( self.angles ) * 100 ); // assures normalized vector is length of "1" + fan_vec = VectorNormalize( fan_vec ); + + while ( true ) + { + dot_x = abs( VectorDot( fan_vec, ( 1, 0, 0 ) ) ); + dot_y = abs( VectorDot( fan_vec, ( 0, 1, 0 ) ) ); + dot_z = abs( VectorDot( fan_vec, ( 0, 0, 1 ) ) ); + + if ( dot_x > 0.9 ) + self RotateVelocity( ( speed, 0, 0 ), time ); + else if ( dot_y > 0.9 ) + self RotateVelocity( ( speed, 0, 0 ), time ); + else if ( dot_z > 0.9 ) + self RotateVelocity( ( 0, speed, 0 ), time ); + else + self RotateVelocity( ( 0, speed, 0 ), time ); + + wait time; + } +} + +triggerTouchThink( enterFunc, exitFunc ) +{ + level endon( "game_ended" ); + self endon("deleted"); + + self.entNum = self GetEntityNumber(); + + while ( true ) + { + self waittill( "trigger", player ); + + if ( !IsPlayer( player ) && !IsDefined( player.finished_spawning ) ) + continue; + + if ( !IsAlive( player ) ) + continue; + + if ( !IsDefined( player.touchTriggers[ self.entNum ] ) ) + player thread playerTouchTriggerThink( self, enterFunc, exitFunc ); + } +} + +playerTouchTriggerThink( trigger, enterFunc, exitFunc ) +{ + trigger endon("deleted"); + + if ( !IsPlayer( self ) ) + self endon( "death" ); + + if ( !isSP() ) + touchName = self.guid; // generate GUID + else + touchName = "player" + GetTime(); // generate GUID + + trigger.touchList[ touchName ] = self; + if ( IsDefined( trigger.moveTracker ) ) + self.moveTrackers++; + + trigger notify( "trigger_enter", self ); + self notify( "trigger_enter", trigger ); + + if ( IsDefined( enterFunc ) ) + self thread [[ enterFunc ]]( trigger ); + + self.touchTriggers[ trigger.entNum ] = trigger; + + while ( IsAlive( self ) && self IsTouching( trigger ) && ( isSP() || !level.gameEnded ) ) + wait( 0.05 ); + + // disconnected player will skip this code + if ( IsDefined( self ) ) + { + self.touchTriggers[ trigger.entNum ] = undefined; + if ( IsDefined( trigger.moveTracker ) ) + self.moveTrackers--; + + self notify( "trigger_leave", trigger ); + + if ( IsDefined( exitFunc ) ) + self thread [[ exitFunc ]]( trigger ); + } + + if ( !isSP() && level.gameEnded ) + return; + + trigger.touchList[ touchName ] = undefined; + trigger notify( "trigger_leave", self ); + + if ( !anythingTouchingTrigger( trigger ) ) + trigger notify( "trigger_empty" ); +} + +movementTracker() +{ + if ( IsDefined( level.DisablemovementTracker ) ) + return; + self endon( "disconnect" ); + + if ( !IsPlayer( self ) ) + self endon( "death" ); + + self.moveTrackers = 0; + self.distMoved = 0; + + for ( ;; ) + { + self waittill( "trigger_enter" ); + + lastOrigin = self.origin; + while ( self.moveTrackers ) + { + self.distMoved = Distance( lastOrigin, self.origin ); + lastOrigin = self.origin; + wait( 0.05 ); + } + + self.distMoved = 0; + } +} + +anythingTouchingTrigger( trigger ) +{ + return( trigger.touchList.size ); +} + +playerTouchingTrigger( player, trigger ) +{ + Assert( IsDefined( trigger.entNum ) ); + return( IsDefined( player.touchTriggers[ trigger.entNum ] ) ); +} + +interactive_tv() +{ + tv_array = GetEntArray( "interactive_tv", "targetname" ); + if ( tv_array.size ) + { + noself_array_call( [ "com_tv2_d", "com_tv1_d", "com_tv1", "com_tv2", "com_tv1_testpattern", "com_tv2_testpattern" ], ::PreCacheModel ); + level.breakables_fx[ "tv_explode" ] = LoadFX( "fx/explosions/tv_explosion" ); + } + level.tv_lite_array = GetEntArray( "interactive_tv_light", "targetname" ); + array_thread( GetEntArray( "interactive_tv", "targetname" ), ::tv_logic ); +} + +tv_logic() +{ + self SetCanDamage( true ); + self.damagemodel = undefined; + self.offmodel = undefined; + + self.damagemodel = "com_tv2_d"; + self.offmodel = "com_tv2"; + self.onmodel = "com_tv2_testpattern"; + if ( IsSubStr( self.model, "1" ) ) + { + self.offmodel = "com_tv1"; + self.onmodel = "com_tv1_testpattern"; + } + + if ( IsDefined( self.target ) ) + { + if ( IsDefined( level.disable_interactive_tv_use_triggers ) ) + { + usetrig = GetEnt( self.target, "targetname" ); + if ( IsDefined( usetrig ) ) + usetrig Delete(); + } + else + { + self.usetrig = GetEnt( self.target, "targetname" ); + self.usetrig UseTriggerRequireLookAt(); + self.usetrig SetCursorHint( "HINT_NOICON" ); + } + } + + array = get_array_of_closest( self.origin, level.tv_lite_array, undefined, undefined, 64 ); + + if ( array.size ) + { + self.lite = array[ 0 ]; + level.tv_lite_array = array_remove( level.tv_lite_array, self.lite ); + self.liteintensity = self.lite GetLightIntensity(); + } + + self thread tv_damage(); + + if ( IsDefined( self.usetrig ) ) + self thread tv_off(); +} + +tv_off() +{ + self.usetrig endon( "death" ); + + while ( 1 ) + { + wait 0.2; + self.usetrig waittill( "trigger" ); + // it would be nice to play a sound here + + self notify( "off" ); + + if ( self.model == self.offmodel ) + { + self SetModel( self.onmodel ); + if ( IsDefined( self.lite ) ) + self.lite SetLightIntensity( self.liteintensity ); + } + else + { + self SetModel( self.offmodel ); + if ( IsDefined( self.lite ) ) + self.lite SetLightIntensity( 0 ); + } + } +} + +tv_damage() +{ + self waittill( "damage", damage, other, direction_vec, P, type ); + + self notify( "off" ); + if ( IsDefined( self.usetrig ) ) + self.usetrig notify( "death" ); + + self SetModel( self.damagemodel ); + + if ( IsDefined( self.lite ) ) + self.lite SetLightIntensity( 0 ); + + PlayFXOnTag( level.breakables_fx[ "tv_explode" ], self, "tag_fx" ); + + self PlaySound( "tv_shot_burst" ); + if ( IsDefined( self.usetrig ) ) + self.usetrig Delete(); +} +// ================================================================================ // +// Sliding Doors // +// ================================================================================ // + +sliding_door() +{ + if( !IsDefined( self.open_time ) ) + self.open_time = 1; + + assert( isdefined( self.target ) ); + + doors = GetEntArray( self.target, "script_linkname" ); + + origins = []; + foreach( d in doors ) + { + if( d.classname == "script_origin" ) + { + origins[origins.size] = d; + continue; + } + d door_init( self.open_time ); + } + doors = array_remove_array( doors, origins ); + + while(1) + { + if( !isdefined( level.characters ) ) + { + wait 1; + continue; + } + + vehicles = Vehicle_GetArray(); + touch_ents = array_combine(level.characters, vehicles); + + touching = 0; + foreach (ent in touch_ents) + { + if( ent IsTouching( self ) ) + { + touching++; + break; //only need one guy touching this + } + } + + if( touching > 0 ) + { + open_all_doors( doors ); + } + else + { + close_time = 1; + thread close_all_doors( doors, close_time ); + } + wait( 0.05 ); + } +} + +door_init( open_time ) +{ + assert( !isdefined( self.start_position ) ); + + // save off original door1 origin + self.start_position = self.origin; + self.sliding_door_state = "closed"; + + // get the open reference from radiant + assertEx( isdefined( self.target ), "mp sliding doors need to have a target ent which is where it should open to" ); + ref = getent( self.target, "targetname" ); + assert( isdefined( ref ) ); + assert( !isdefined( self.open_position ) ); + + self.open_position = ref.origin; + self.open_velocity = Distance( self.open_position, self.origin ) / open_time; +} + +open_all_doors( door_arr ) +{ + foreach( door in door_arr ) + { + if( door.sliding_door_state == "open" || door.sliding_door_state == "opening" ) + continue; + + door thread open_door(); + } +} + +open_door() +{ + //this door was not set up correctly + assert( IsDefined( self.open_position ) ); + + self.sliding_door_state = "opening"; + + open_time = Distance( self.origin, self.open_position ) / self.open_velocity; + + if( open_time < .05 ) + { + open_time = .05; + } + + assert( isdefined( self.start_position ) ); + assert( isdefined( self.open_position ) ); + self MoveTo( self.open_position, open_time ); + + self playsound ( "glass_door_open"); + + wait( open_time ); + + self.sliding_door_state = "open"; +} + +close_all_doors( door_arr, close_time ) +{ + foreach( door in door_arr ) + { + if( door.sliding_door_state == "closed" || door.sliding_door_state == "opening" ) + continue; + + assert( isdefined( door.start_position ) ); + door MoveTo( door.start_position, close_time ); + + self playsound ( "glass_door_close"); + + door.sliding_door_state = "closed"; + } +} diff --git a/raw/common_scripts/_elevator.gsc b/raw/common_scripts/_elevator.gsc new file mode 100644 index 0000000..8852bdb --- /dev/null +++ b/raw/common_scripts/_elevator.gsc @@ -0,0 +1,1286 @@ +//#include maps\mp\_utility; +#include common_scripts\utility; + +init() +{ + if ( getdvar( "scr_elevator_disabled" ) == "1" ) + return; + + // skip if no elevators in this level + elevator_groups = getentarray( "elevator_group", "targetname" ); + if ( !isdefined( elevator_groups ) ) + return; + if ( !elevator_groups.size ) + return; + + // Press and hold &&1 to call elevator. + precacheString( &"ELEVATOR_CALL_HINT" ); + // Press and hold &&1 to use elevator. + precacheString( &"ELEVATOR_USE_HINT" ); + // Press and hold &&1 to select floor. + precacheString( &"ELEVATOR_FLOOR_SELECT_HINT" ); + + precacheMenu( "elevator_floor_selector" ); + + thread elevator_update_global_dvars(); + + // find and build all elevators in the level + level.elevators = []; + + // elevator construction dvars: + // vertical units for "call button" to link near by elevator per floor + level.elevator_callbutton_link_v = elevator_get_dvar_int( "scr_elevator_callbutton_link_v", "96" ); + // horizontal units for "call button" to link near by elevator per floor + level.elevator_callbutton_link_h = elevator_get_dvar_int( "scr_elevator_callbutton_link_h", "256" ); + + build_elevators(); + position_elevators(); + elevator_call(); + + if ( !level.elevators.size ) + return; + + foreach ( elevator in level.elevators ) + { + elevator thread elevator_think(); + elevator thread elevator_sound_think(); + } + + thread elevator_debug(); +} + +elevator_update_global_dvars() +{ + while ( 1 ) + { + level.elevator_accel = elevator_get_dvar( "scr_elevator_accel", "0.2" ); // acceleration time in seconds + level.elevator_decel = elevator_get_dvar( "scr_elevator_decel", "0.2" ); // deceleration time in seconds + level.elevator_music = elevator_get_dvar_int( "scr_elevator_music", "1" ); // elevator music + level.elevator_speed = elevator_get_dvar_int( "scr_elevator_speed", "96" ); // units per second + level.elevator_innerdoorspeed = elevator_get_dvar_int( "scr_elevator_innerdoorspeed", "14" ); // inner door speed + level.elevator_outterdoorspeed = elevator_get_dvar_int( "scr_elevator_outterdoorspeed", "16" ); // outter door speed + level.elevator_return = elevator_get_dvar_int( "scr_elevator_return", "0" ); // 1: elevator returns to original floor + level.elevator_waittime = elevator_get_dvar_int( "scr_elevator_waittime", "6" ); // wait in seconds before closing door + level.elevator_aggressive_call = elevator_get_dvar_int( "scr_elevator_aggressive_call", "0" ); // calls all available elevators to floor + level.elevator_debug = elevator_get_dvar_int( "debug_elevator", "0" ); // 2: full 1: simple, 0: debug off + + // mp & sp default differences: + if ( isSP() ) + { + level.elevator_motion_detection = elevator_get_dvar_int( "scr_elevator_motion_detection", "0" );// calls elevators via motion detection + } + else + { + level.elevator_motion_detection = elevator_get_dvar_int( "scr_elevator_motion_detection", "1" );// calls elevators via motion detection + } + + wait 1; + } +} + +//==========================================================================// +// === ELEVATOR LOGIC === // +//==========================================================================// + +elevator_think() +{ + // self is elevator, self.e[] + self elevator_fsm( "[A]" ); +} + +elevator_call() +{ + foreach ( callbutton in level.elevator_callbuttons ) + callbutton thread monitor_callbutton(); +} + +floor_override( inside_trig ) +{ + self endon( "elevator_moving" ); + + self.floor_override = 0; + self.overrider = undefined; + + while ( 1 ) + { + inside_trig waittill( "trigger", player ); + self.floor_override = 1; + self.overrider = player; + break; + } + self notify( "floor_override" ); +} + +elevator_fsm( state ) +{ + /* finite state machine + + state A: rest + state B: closing doors - interrupt threaded + state C: opening doors + state D: moving elevator + ?1: if not resting at initial floor + + .-------delay-------. + | | + ?1 V + start --> [A] --inside_trig--> [B] --delay--> [D] + ^ | | + | door_trig | + | | | + | V | + '-----delay------- [C] <---delay---' + */ + + /* self is elevator, self.e[] */ + + self.eState = state; + + door_trig = self get_housing_door_trigger(); // triggers door interrupt + inside_trig = self get_housing_inside_trigger(); // presence + + while ( 1 ) + { + //state A: rest + if ( self.eState == "[A]" ) + { + // ?1: if not resting at initial floor + if ( level.elevator_return && ( self get_curFloor() != self get_initFloor() ) ) + { + self.moveto_floor = self get_initFloor(); + self thread floor_override( inside_trig ); + self waittill_or_timeout( "floor_override", level.elevator_waittime ); + + if ( self.floor_override && isdefined( self.overrider ) && isPlayer( self.overrider ) ) + self get_floor( self.overrider ); + + self.eState = "[B]"; + continue; + } + + // wait for player use trigger + while ( 1 ) + { + // if elevator already has destination but interrupted, it should continue previous order + if ( self.moveto_floor == self get_curFloor() ) + param = inside_trig discrete_waittill( "trigger" ); + else + param = "elevator_called"; // as if called onto the destination floor + + // elevator call hack + if ( isString( param ) && ( param == "elevator_called" ) && ( self.moveto_floor != self get_curFloor() ) ) + { + self.eState = "[B]"; + break; + } + + if ( isdefined( param ) && isPlayer( param ) && isAlive( param ) ) + { + isTouching_trigger = ( param istouching( inside_trig ) ); + isTouching_motion_trigger = ( isdefined( inside_trig.motion_trigger ) && param istouching( inside_trig.motion_trigger ) ); + player_isTouching_trigger = ( isTouching_trigger || isTouching_motion_trigger ); + + if ( player_isTouching_trigger ) + { + player = param; + self get_floor( player ); + + if ( self.moveto_floor == self get_curFloor() ) + continue; + + self.eState = "[B]"; + break; + } + } + } + } + + //state B: closing doors - interrupt threaded + if ( self.eState == "[B]" ) + { + self thread elevator_interrupt( door_trig ); + floor_num = self get_curFloor(); + + // call to close doors + self thread close_inner_doors(); + self thread close_outer_doors( floor_num ); + + self waittill_any( "closed_inner_doors", "interrupted" ); // Wait for inner doors to close for the safty of our valued customers. + + if ( self.elevator_interrupted ) + { + self.eState = "[C]"; + continue; + } + + self.eState = "[D]"; + continue; + } + + //state C: opening doors + if ( self.eState == "[C]" ) + { + floor_num = self get_curFloor(); + + self thread open_inner_doors(); + self thread open_outer_doors( floor_num ); + + self waittill( "opened_floor_" + floor_num + "_outer_doors" ); + + if ( self.elevator_interrupted ) + { + self.eState = "[B]"; + continue; + } + + self.eState = "[A]"; + continue; + } + + //state D: moving elevator + if ( self.eState == "[D]" ) + { + assertex( isdefined( self.moveto_floor ), "Missing destination floor number" ); + + if ( self.moveto_floor != self get_curFloor() ) + { + self thread elevator_move( self.moveto_floor ); + self waittill( "elevator_moved" ); + } + + self.eState = "[C]"; + continue; + } + } +} + +monitor_callbutton() +{ + while ( 1 ) + { + // call button used by player + player = self discrete_waittill( "trigger" ); + + call_floor = undefined; + call_elevators = []; + // fake loop, always loop only once + foreach ( idx, linked_elevators in self.e ) + { + call_floor = idx; + call_elevators = linked_elevators; + } + assert( isdefined( call_floor ) && isdefined( call_elevators ) && call_elevators.size ); + + elevator_called = 0; + + // prefilter elevators to see if any already at this floor + foreach ( elevator in call_elevators ) + { + moving = elevator elevator_floor_update(); + + // if in non-aggressive mode; this elevator is already stationary at the requested floor, we skip! + if ( !level.elevator_aggressive_call && !moving ) + { + if ( elevator get_curFloor() == call_floor ) + { + elevator_called = 1; + call_elevators = []; + break; + } + } + } + + // check all elevators to see if any can be called to this floor + foreach ( elevator in call_elevators ) + { + if ( elevator.eState == "[A]" ) + { + // call only elevators in a state of rest [A] + elevator call_elevator( call_floor ); + + elevator_called = 1; + + // aggressive call + if ( !level.elevator_aggressive_call ) + break; + } + } + + if ( elevator_called ) + self playsound( "elev_bell_ding" ); + } +} + +call_elevator( call_floor ) +{ + self.moveto_floor = call_floor; + + // call elevator via trigger hack + inside_trigger = self get_housing_inside_trigger(); + inside_trigger notify( "trigger", "elevator_called" ); + if ( level.elevator_motion_detection ) + inside_trigger.motion_trigger notify( "trigger", "elevator_called" ); +} + +// opens menu to select floors +get_floor( player ) +{ + // skips popup menu if only two floors present + bifloor = self get_outer_doorsets(); + if ( bifloor.size == 2 ) + { + curFloor = self get_curFloor(); + self.moveto_floor = !curFloor; + return; + } + + player openpopupmenu( "elevator_floor_selector" ); + player setClientDvar( "player_current_floor", self get_curFloor() ); + + while ( 1 ) + { + player waittill( "menuresponse", menu, response ); + + if ( menu == "elevator_floor_selector" ) + { + if ( response != "none" ) + self.moveto_floor = int( response ); + + break; + } + } +} + +elevator_interrupt( door_trig ) +{ + self notify( "interrupt_watch" ); + level notify( "elevator_interior_button_pressed" ); + self endon( "interrupt_watch" ); // no duplicate interrupts + self endon( "elevator_moving" ); + + self.elevator_interrupted = 0; + wait 0.5;// buffer time to have the door jitter... + door_trig waittill( "trigger", player ); + + /*while ( 1 ) + { + wait 0.5; // buffer time to have the door jitter... realisticaly, im serious! + + door_trig waittill( "trigger", player ); + if ( isdefined( player ) && isPlayer( player ) ) + break; + }*/ + + self notify( "interrupted" ); + self.elevator_interrupted = 1; + + //self elevator_fsm( "[C]" ); +} + +// returns if elevator is moving, also updates the floor number if not moving +elevator_floor_update() +{ + mainframe = self get_housing_mainframe(); + cur_pos = mainframe.origin; + + moving = 1; + foreach ( idx, eFloor in self get_outer_doorsets() ) + { + floor_pos = self.e[ "floor" + idx + "_pos" ]; + if ( cur_pos == floor_pos ) + { + self.e[ "current_floor" ] = idx; + moving = 0; + } + } + + return moving; +} + +elevator_sound_think() +{ + // self is elevator, self.e[] + // play elevator sounds on notify of behavior + + // always play musak + musak_model = self get_housing_musak_model(); + + if ( level.elevator_music && isdefined( musak_model ) ) + musak_model playloopsound( "elev_musak_loop" ); + + self thread listen_for ( "closing_inner_doors" ); + self thread listen_for ( "opening_inner_doors" ); + self thread listen_for ( "closed_inner_doors" ); + self thread listen_for ( "opened_inner_doors" ); + + foreach ( idx, eFloor in self get_outer_doorsets() ) + { + self thread listen_for ( "closing_floor_" + idx + "_outer_doors" ); + self thread listen_for ( "opening_floor_" + idx + "_outer_doors" ); + self thread listen_for ( "closed_floor_" + idx + "_outer_doors" ); + self thread listen_for ( "opened_floor_" + idx + "_outer_doors" ); + } + + self thread listen_for ( "interrupted" ); + //self thread listen_for( "interrupt_watch" ); + self thread listen_for ( "elevator_moving" ); + self thread listen_for ( "elevator_moved" ); +} + +/* +-elev_musak_loop,,music/mx_musak_stolen_elev_proc1.wav,1,1,foley,1,1,120,500,effects1,,,rlooping,,all_mp +-elev_door_open,,events/elev_door_open1.wav,1,1,foley,0.95,1.05,120,2500,voice,,,,,all_mp +-elev_door_close,,events/elev_door_close1.wav,1,1,foley,0.95,1.05,120,2500,voice,,,,,all_mp +-elev_door_interupt,,events/elev_door_interupt1.wav,1,1,foley,0.95,1.05,120,2500,voice,,,,,all_mp +-elev_bell_ding,,events/elev_bell_ding1.wav,1,1,foley,1,1,120,2500,voice,,,,,all_mp +-elev_run_start,,events/elev_run_start01.wav,1,1,foley,1,1,120,500,voice,,,,,all_mp +-elev_run_loop,,events/elev_run_loop01.wav,1,1,foley,1,1,120,500,voice,,,rlooping,,all_mp +-elev_run_end,,events/elev_run_stop01.wav,1,1,foley,1,1,120,500,voice,,,,,all_mp +*/ + +listen_for ( msg ) +{ + while ( 1 ) + { + self waittill( msg ); + mainframe = self get_housing_mainframe(); + + if ( issubstr( msg, "closing_" ) ) + mainframe playsound( "elev_door_close" ); + + if ( issubstr( msg, "opening_" ) ) + mainframe playsound( "elev_door_open" ); + + if ( msg == "elevator_moving" ) + { + mainframe playsound( "elev_run_start" ); + mainframe playloopsound( "elev_run_loop" ); + } + + if ( msg == "interrupted" ) + mainframe playsound( "elev_door_interupt" ); + + if ( msg == "elevator_moved" ) + { + mainframe stoploopsound( "elev_run_loop" ); + mainframe playsound( "elev_run_end" ); + mainframe playsound( "elev_bell_ding" ); + } + + //if ( isdefined( level.players[0] ) && level.elevator_debug ) + // level.players[0] iprintln( "(e" + self.e[ "id" ] + ") " + msg ); + } +} + +//==========================================================================// +// === MOVEMENTS === // +//==========================================================================// + +// position elevators +position_elevators() +{ + // closing all outer doors on floors elevator isnt at + foreach ( e, elevator in level.elevators ) + { + elevator.moveto_floor = elevator get_curFloor(); + + foreach ( floor_num, outer_doorset in elevator get_outer_doorsets() ) + { + if ( elevator get_curFloor() != floor_num ) + elevator thread close_outer_doors( floor_num ); + } + } +} + +elevator_move( floor_num ) +{ + // self is elevator, self.e[] + self notify( "elevator_moving" ); + self endon( "elevator_moving" ); + + mainframe = self get_housing_mainframe(); + delta_vec = self.e[ "floor" + floor_num + "_pos" ] - mainframe.origin; + + speed = level.elevator_speed; + dist = abs( distance( self.e[ "floor" + floor_num + "_pos" ], mainframe.origin ) ); + moveTime = dist / speed; + + // move housing: + mainframe moveTo( mainframe.origin + delta_vec, moveTime, moveTime * level.elevator_accel, moveTime * level.elevator_decel ); + + // move doors and triggers and other script models + foreach ( part in self get_housing_children() ) + { + moveto_pos = part.origin + delta_vec; + + if ( !issubstr( part.classname, "trigger_" ) ) + part moveTo( moveto_pos, moveTime, moveTime * level.elevator_accel, moveTime * level.elevator_decel ); + else + part.origin = moveto_pos; + } + + // making sure elevator housing gets to destination floor + self waittill_finish_moving( mainframe, self.e[ "floor" + floor_num + "_pos" ] ); + + self notify( "elevator_moved" ); +} + +close_inner_doors() +{ + // self is elevator set + self notify( "closing_inner_doors" ); + self endon( "closing_inner_doors" ); // when interrupted + self endon( "opening_inner_doors" ); // when interrupted + + left_door = self get_housing_leftdoor(); // ent + right_door = self get_housing_rightdoor(); // ent + + mainframe = self get_housing_mainframe(); // ent + old_closed_pos = self get_housing_closedpos(); // vec + closed_pos = ( old_closed_pos[ 0 ], old_closed_pos[ 1 ], mainframe.origin[ 2 ] ); // adjusted closed position after floor change + + speed = level.elevator_innerdoorspeed; // scaler + dist = abs( distance( left_door.origin, closed_pos ) ); // scaler + moveTime = dist / speed; // scaler + + left_door moveTo( closed_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + right_door moveTo( closed_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + + self waittill_finish_moving( left_door, closed_pos, right_door, closed_pos ); + self notify( "closed_inner_doors" ); +} + +open_inner_doors() +{ + // self is elevator set + self notify( "opening_inner_doors" ); + self endon( "opening_inner_doors" ); // when interrupted + + left_door = self get_housing_leftdoor(); // ent + right_door = self get_housing_rightdoor(); // ent + + mainframe = self get_housing_mainframe(); // ent + old_left_opened_pos = self get_housing_leftdoor_opened_pos(); // vec + old_right_opened_pos = self get_housing_rightdoor_opened_pos(); // vec + + left_opened_pos = ( old_left_opened_pos[ 0 ], old_left_opened_pos[ 1 ], mainframe.origin[ 2 ] ); + right_opened_pos = ( old_right_opened_pos[ 0 ], old_right_opened_pos[ 1 ], mainframe.origin[ 2 ] ); + + speed = level.elevator_innerdoorspeed; // scaler + dist = abs( distance( left_opened_pos, right_opened_pos ) * 0.5 ); // scaler + moveTime = ( dist / speed ) * 0.5; // scaler + + left_door moveTo( left_opened_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + right_door moveTo( right_opened_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + + self waittill_finish_moving( left_door, left_opened_pos, right_door, right_opened_pos ); + self notify( "opened_inner_doors" ); +} + +// close outer elevator doors per floor +close_outer_doors( floor_num ) +{ + // self is elevator set + self notify( "closing_floor_" + floor_num + "_outer_doors" ); + self endon( "closing_floor_" + floor_num + "_outer_doors" ); // when interrupted + self endon( "opening_floor_" + floor_num + "_outer_doors" ); // when interrupted + + left_door = self get_outer_leftdoor( floor_num ); // ent + right_door = self get_outer_rightdoor( floor_num ); // ent + + left_opened_pos = self get_outer_leftdoor_openedpos( floor_num ); // vec + closed_pos = self get_outer_closedpos( floor_num ); // vec + + speed = level.elevator_outterdoorspeed; // scaler + dist = abs( distance( left_opened_pos, closed_pos ) ); // scaler + moveTime = dist / speed; // scaler + + left_door moveTo( closed_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + right_door moveTo( closed_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + + self waittill_finish_moving( left_door, closed_pos, right_door, closed_pos ); + self notify( "closed_floor_" + floor_num + "_outer_doors" ); +} + +// open outer elevator doors per floor +open_outer_doors( floor_num ) +{ + // self is elevator set + level notify( "elevator_doors_opening" ); + self notify( "opening_floor_" + floor_num + "_outer_doors" ); + self endon( "opening_floor_" + floor_num + "_outer_doors" ); // when interrupted + + left_door = self get_outer_leftdoor( floor_num ); // ent + right_door = self get_outer_rightdoor( floor_num ); // ent + + left_opened_pos = self get_outer_leftdoor_openedpos( floor_num ); // vec + right_opened_pos = self get_outer_rightdoor_openedpos( floor_num ); // vec + closed_pos = self get_outer_closedpos( floor_num ); // vec + + speed = level.elevator_outterdoorspeed; // scaler + dist = abs( distance( left_opened_pos, closed_pos ) ); // scaler + moveTime = ( dist / speed ) * 0.5; // scaler + + left_door moveTo( left_opened_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + right_door moveTo( right_opened_pos, moveTime, moveTime * 0.1, moveTime * 0.25 ); + + self waittill_finish_moving( left_door, left_opened_pos, right_door, right_opened_pos ); + self notify( "opened_floor_" + floor_num + "_outer_doors" ); +} + +//==========================================================================// +// === BUILD ELEVATORS === // +//==========================================================================// + +// builds elevators into 'level.elevators' struct array containing parts list +build_elevators() +{ + // triggers that enclose elevator, to distinguish between elevators + elevator_groups = getentarray( "elevator_group", "targetname" ); + assertex( isdefined( elevator_groups ) && ( elevator_groups.size ), "Radiant: Missing elevator bounding origins" ); + + elevator_housings = getentarray( "elevator_housing", "targetname" ); + assertex( isdefined( elevator_housings ) && ( elevator_housings.size >= elevator_groups.size ), "Fail! Missing the whole elevator, script_brushmodel with [targetname = elevator_housing] must be correctly placed" ); + + // sets of elevator outter door prefabs placed + elevator_doorsets = getentarray( "elevator_doorset", "targetname" ); + assertex( isdefined( elevator_doorsets ) && ( elevator_doorsets.size >= elevator_groups.size ), "Radiant: Missing elevator door(s)" ); + + // build found elevators + foreach ( elevator_bound in elevator_groups ) + { + elevator_bound_end = getent( elevator_bound.target, "targetname" ); + + min_max_xy = []; + min_max_xy[ 0 ] = min( elevator_bound.origin[ 0 ], elevator_bound_end.origin[ 0 ] );// min_x + min_max_xy[ 1 ] = max( elevator_bound.origin[ 0 ], elevator_bound_end.origin[ 0 ] );// max_x + min_max_xy[ 2 ] = min( elevator_bound.origin[ 1 ], elevator_bound_end.origin[ 1 ] );// min_y + min_max_xy[ 3 ] = max( elevator_bound.origin[ 1 ], elevator_bound_end.origin[ 1 ] );// max_y + + parts = spawnstruct(); + parts.e[ "id" ] = level.elevators.size; + + // === build elevator housing === + /* + Elevator's mainframe brushmodel + + script_brushmodel targeting: + "elevator_housing" -> left door -> right door -> door trigger -> inside trigger + + Housing is the body that moves between floors + */ + parts.e[ "housing" ] = []; + parts.e[ "housing" ][ "mainframe" ] = []; + + foreach ( elevator_housing in elevator_housings ) + { + if ( elevator_housing isInbound( min_max_xy ) ) + { + parts.e[ "housing" ][ "mainframe" ][ parts.e[ "housing" ][ "mainframe" ].size ] = elevator_housing; + + if ( elevator_housing.classname == "script_model" ) + continue; + + if ( elevator_housing.code_classname == "light" ) + continue; + + inner_leftdoor = getent( elevator_housing.target, "targetname" ); + parts.e[ "housing" ][ "left_door" ] = inner_leftdoor; + parts.e[ "housing" ][ "left_door_opened_pos" ] = inner_leftdoor.origin; + + inner_rightdoor = getent( inner_leftdoor.target, "targetname" ); + parts.e[ "housing" ][ "right_door" ] = inner_rightdoor; + parts.e[ "housing" ][ "right_door_opened_pos" ] = inner_rightdoor.origin; + + inner_door_closed_pos = ( inner_leftdoor.origin - inner_rightdoor.origin ) * ( 0.5, 0.5, 0.5 ) + inner_rightdoor.origin; + parts.e[ "housing" ][ "door_closed_pos" ] = inner_door_closed_pos; + + door_trigger = getent( inner_rightdoor.target, "targetname" ); + parts.e[ "housing" ][ "door_trigger" ] = door_trigger; + + inside_trigger = getent( door_trigger.target, "targetname" ); + parts.e[ "housing" ][ "inside_trigger" ] = inside_trigger; + inside_trigger make_discrete_trigger(); + + // for motion triggers + inside_trigger.motion_trigger = spawn( "trigger_radius", elevator_housing.origin, 0, 64, 128 ); + assert( isdefined( inside_trigger.motion_trigger ) ); + } + } + assert( isdefined( parts.e[ "housing" ] ) ); + + // === build elevator outer doorsets === + /* + Elevator's outer door sets ( per floor ) are chained together from a script_origin + targeting to the left door, then left door targets to right door + + Outer door sets are bodies that only moves to open/close + */ + parts.e[ "outer_doorset" ] = []; + foreach ( elevator_doorset in elevator_doorsets ) + { + if ( elevator_doorset isInbound( min_max_xy ) ) + { + + // closed for lighting is for compiling lighting in the closed position rather than the open position. + door_starts_closed = isdefined( elevator_doorset.script_noteworthy ) && elevator_doorset.script_noteworthy == "closed_for_lighting"; + + door_set_id = parts.e[ "outer_doorset" ].size; + + parts.e[ "outer_doorset" ][ door_set_id ] = []; + parts.e[ "outer_doorset" ][ door_set_id ][ "door_closed_pos" ] = elevator_doorset.origin; + + leftdoor = getent( elevator_doorset.target, "targetname" ); + parts.e[ "outer_doorset" ][ door_set_id ][ "left_door" ] = leftdoor; + parts.e[ "outer_doorset" ][ door_set_id ][ "left_door_opened_pos" ] = leftdoor.origin; + + rightdoor = getent( leftdoor.target, "targetname" ); + parts.e[ "outer_doorset" ][ door_set_id ][ "right_door" ] = rightdoor; + parts.e[ "outer_doorset" ][ door_set_id ][ "right_door_opened_pos" ] = rightdoor.origin; + + //put them back into the would be positions + if( door_starts_closed ) + { + left_door_vec = elevator_doorset.origin - leftdoor.origin; + elevator_doorset.origin = leftdoor.origin; + leftdoor.origin += left_door_vec; + rightdoor.origin -= left_door_vec; + parts.e[ "outer_doorset" ][ door_set_id ][ "door_closed_pos" ] = elevator_doorset.origin; + parts.e[ "outer_doorset" ][ door_set_id ][ "left_door_opened_pos" ] = leftdoor.origin; + parts.e[ "outer_doorset" ][ door_set_id ][ "right_door_opened_pos" ] = rightdoor.origin; + } + } + } + assert( isdefined( parts.e[ "outer_doorset" ] ) ); + + // bubble sort floors + for ( i = 0; i < parts.e[ "outer_doorset" ].size - 1; i++ ) + { + for ( j = 0; j < parts.e[ "outer_doorset" ].size - 1 - i; j++ ) + { + if ( parts.e[ "outer_doorset" ][ j + 1 ][ "door_closed_pos" ][ 2 ] < parts.e[ "outer_doorset" ][ j ][ "door_closed_pos" ][ 2 ] ) + { + //swap j-1 with j + temp_left_door = parts.e[ "outer_doorset" ][ j ][ "left_door" ]; + temp_left_door_opened_pos = parts.e[ "outer_doorset" ][ j ][ "left_door_opened_pos" ]; + temp_right_door = parts.e[ "outer_doorset" ][ j ][ "right_door" ]; + temp_right_door_opened_pos = parts.e[ "outer_doorset" ][ j ][ "right_door_opened_pos" ]; + temp_closed_pos = parts.e[ "outer_doorset" ][ j ][ "door_closed_pos" ]; + + parts.e[ "outer_doorset" ][ j ][ "left_door" ] = parts.e[ "outer_doorset" ][ j + 1 ][ "left_door" ]; + parts.e[ "outer_doorset" ][ j ][ "left_door_opened_pos" ] = parts.e[ "outer_doorset" ][ j + 1 ][ "left_door_opened_pos" ]; + parts.e[ "outer_doorset" ][ j ][ "right_door" ] = parts.e[ "outer_doorset" ][ j + 1 ][ "right_door" ]; + parts.e[ "outer_doorset" ][ j ][ "right_door_opened_pos" ] = parts.e[ "outer_doorset" ][ j + 1 ][ "right_door_opened_pos" ]; + parts.e[ "outer_doorset" ][ j ][ "door_closed_pos" ] = parts.e[ "outer_doorset" ][ j + 1 ][ "door_closed_pos" ]; + + parts.e[ "outer_doorset" ][ j + 1 ][ "left_door" ] = temp_left_door; + parts.e[ "outer_doorset" ][ j + 1 ][ "left_door_opened_pos" ] = temp_left_door_opened_pos; + parts.e[ "outer_doorset" ][ j + 1 ][ "right_door" ] = temp_right_door; + parts.e[ "outer_doorset" ][ j + 1 ][ "right_door_opened_pos" ] = temp_right_door_opened_pos; + parts.e[ "outer_doorset" ][ j + 1 ][ "door_closed_pos" ] = temp_closed_pos; + } + } + } + + // === build elevator floor pos === + floor_pos = []; + foreach ( i, doorset in parts.e[ "outer_doorset" ] ) + { + mainframe = parts get_housing_mainframe(); + + floor_pos = ( mainframe.origin[ 0 ], mainframe.origin[ 1 ], doorset[ "door_closed_pos" ][ 2 ] ); + parts.e[ "floor" + i + "_pos" ] = floor_pos; + + if ( mainframe.origin == floor_pos ) + { + parts.e[ "initial_floor" ] = i; + parts.e[ "current_floor" ] = i; + } + } + + // === save build info === + level.elevators[ level.elevators.size ] = parts; + + // trash all script origins no longer needed + elevator_bound delete(); + elevator_bound_end delete(); + } + foreach ( elevator_doorset in elevator_doorsets ) + elevator_doorset delete(); + + build_call_buttons(); + + if ( !level.elevator_motion_detection ) + setup_hints(); + + // turn on all primary lights for elevators if they have em + foreach ( elevator in level.elevators ) + { + pLights = elevator get_housing_primarylight(); + if ( isdefined( pLights ) && pLights.size ) + { + foreach ( pLight in pLights ) + pLight setlightintensity( 0.75 ); + } + } +} + +build_call_buttons() +{ + level.elevator_callbuttons = getentarray( "elevator_call", "targetname" ); + assertex( isdefined( level.elevator_callbuttons ) && ( level.elevator_callbuttons.size > 1 ), "Missing or not enough elevator call buttons" ); + + // per call button + foreach ( callbutton in level.elevator_callbuttons ) + { + callbutton.e = []; + callbutton_v_vec = ( 0, 0, callbutton.origin[ 2 ] ); + callbutton_h_vec = ( callbutton.origin[ 0 ], callbutton.origin[ 1 ], 0 ); + + temp_elevator_list = []; + // per elevator + foreach ( e_idx, elevator in level.elevators ) + { + // per floor + foreach ( f_idx, eFloor in elevator get_outer_doorsets() ) + { + v_vec = ( 0, 0, elevator.e[ "floor" + f_idx + "_pos" ][ 2 ] ); + h_vec = ( elevator.e[ "floor" + f_idx + "_pos" ][ 0 ], elevator.e[ "floor" + f_idx + "_pos" ][ 1 ], 0 ); + + if ( abs( distance( callbutton_v_vec, v_vec ) ) <= level.elevator_callbutton_link_v ) + { + if ( abs( distance( callbutton_h_vec, h_vec ) ) <= level.elevator_callbutton_link_h ) + { + temp_elevator_list[ temp_elevator_list.size ] = elevator; // build list of elevators linked on floor f_idx + callbutton.e[ f_idx ] = temp_elevator_list; + } + } + } + } + callbutton make_discrete_trigger(); + assertex( isdefined( callbutton.e ) && callbutton.e.size, "Elevator call button at " + callbutton.origin + " failed to grab near by elevators, placed too far?" ); + + // build motion detection triggers for motion triggered calls + callbutton.motion_trigger = spawn( "trigger_radius", callbutton.origin + ( 0, 0, -32 ), 0, 32, 64 ); + } +} + +setup_hints() +{ + // elevator inside use trigger hint + foreach ( elevator in level.elevators ) + { + use_trig = elevator get_housing_inside_trigger(); + floors = elevator get_outer_doorsets(); + num_of_floors = floors.size; + + use_trig SetCursorHint( "HINT_NOICON" ); + if ( num_of_floors > 2 ) + // Press and hold &&1 to select floor. + use_trig setHintString( &"ELEVATOR_FLOOR_SELECT_HINT" ); + else + // Press and hold &&1 to use elevator. + use_trig setHintString( &"ELEVATOR_USE_HINT" ); + } + + // elevator call button hint + foreach ( callbutton in level.elevator_callbuttons ) + { + callbutton SetCursorHint( "HINT_NOICON" ); + // Press and hold &&1 to call elevator. + callbutton setHintString( &"ELEVATOR_CALL_HINT" ); + } +} + +//==========================================================================// +// === HELPERS === // +//==========================================================================// + +// setup discrete trigger for discrete waittill +make_discrete_trigger() +{ + self.enabled = 1; // enable now for disabling trigger + self disable_trigger(); // disable trigger, enable again only when waittill on +} + +// trigger only exist when waittill upon +discrete_waittill( msg ) +{ + assert( isdefined( self.motion_trigger ) ); + + self enable_trigger(); // this is for disabling trigger immediately after triggering + + if ( level.elevator_motion_detection ) + self.motion_trigger waittill( msg, param ); + else + self waittill( msg, param ); + + self disable_trigger(); + return param; +} + + +enable_trigger() +{ + if ( !self.enabled ) + { + self.enabled = 1; + self.origin += ( 0, 0, 10000 ); + + if ( isdefined( self.motion_trigger ) ) + self.motion_trigger.origin += ( 0, 0, 10000 ); + } +} + +disable_trigger() +{ + self notify( "disable_trigger" ); + if ( self.enabled ) + self thread disable_trigger_helper(); +} + +disable_trigger_helper() +{ + self endon( "disable_trigger" ); + self.enabled = 0; + wait 1.5; + self.origin += ( 0, 0, -10000 ); + + if ( isdefined( self.motion_trigger ) ) + self.motion_trigger.origin += ( 0, 0, -10000 ); +} + +// ========== OUTER DOOR SETS =========== +// returns array of outer doorsets for this floor - ent[] +get_outer_doorset( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ]; +} + +// returns array of floors of doorsets - int[] +get_outer_doorsets() +{ + return self.e[ "outer_doorset" ]; +} + +// returns outer door closed position for this floor - vec +get_outer_closedpos( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ][ "door_closed_pos" ]; +} + +// returns left door entity for this floor - ent +get_outer_leftdoor( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ][ "left_door" ]; +} + +// returns right door entity for this floor - ent +get_outer_rightdoor( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ][ "right_door" ]; +} + +// returns left door opened position for this floor - vec +get_outer_leftdoor_openedpos( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ][ "left_door_opened_pos" ]; +} + +// returns right door opened position for this floor - vec +get_outer_rightdoor_openedpos( floor_num ) +{ + return self.e[ "outer_doorset" ][ floor_num ][ "right_door_opened_pos" ]; +} + +// ========= HOUSING ========= +// returns all the entities that will move when the elevator moves between floors - ent[] +get_housing_children() +{ + children = []; + + door_trig = self get_housing_door_trigger(); + use_trig = self get_housing_inside_trigger(); + motion_trig = use_trig.motion_trigger; + left_door = self get_housing_leftdoor(); + right_door = self get_housing_rightdoor(); + + children[ children.size ] = door_trig; + children[ children.size ] = use_trig; + children[ children.size ] = left_door; + children[ children.size ] = right_door; + + if ( isdefined( motion_trig ) ) + children[ children.size ] = motion_trig; + + // append script models as children of elevator moving body + script_models = self get_housing_models(); + foreach ( eModel in script_models ) + children[ children.size ] = eModel; + + // append primary light(s) as children of elevator moving body + primarylights = get_housing_primarylight(); + foreach ( pLight in primarylights ) + children[ children.size ] = pLight; + + return children; +} + +// returns only the mainframe script_brushmodel, there can only be one per elevator! - ent +get_housing_mainframe() +{ + parts = self.e[ "housing" ][ "mainframe" ]; + + housing_model = undefined; + foreach ( part in parts ) + { + if ( part.classname != "script_model" && part.code_classname != "light" ) + { + assertex( !isdefined( housing_model ), "Fail! Found more than one elevator housing script_brushmodels per elevator" ); + housing_model = part; + } + } + assertex( isdefined( housing_model ), "Epic fail! No elevator housing script_brushmodel found" ); + return housing_model; +} + +// returns an array of script_models used as part of the elevator prefab - ent[] +get_housing_models() +{ + parts = self.e[ "housing" ][ "mainframe" ]; + temp_model_array = []; + + foreach ( part in parts ) + { + if ( part.classname == "script_model" ) + temp_model_array[ temp_model_array.size ] = part; + } + return temp_model_array; +} + +// returns an array of lights - ent[] +get_housing_primarylight() +{ + parts = self.e[ "housing" ][ "mainframe" ]; + temp_primarylights = []; + + foreach ( part in parts ) + { + if ( part.code_classname == "light" ) + temp_primarylights[ temp_primarylights.size ] = part; + } + return temp_primarylights; +} + +// returns a single model to play elevator music on, this must exist and only one! - ent +get_housing_musak_model() +{ + models = self get_housing_models(); + musak_model = undefined; + + foreach ( eModel in models ) + { + if ( isdefined( eModel.script_noteworthy ) && eModel.script_noteworthy == "play_musak" ) + musak_model = eModel; + } + //assertex( isdefined( musak_model ), "Fail! Missing script_model to play elevator music on, [script_noteworthy = play_musak]" ); + + return musak_model; +} + +// returns interrupt trigger for elevator doors - ent trig +get_housing_door_trigger() +{ + return self.e[ "housing" ][ "door_trigger" ]; +} + +// returns trigger for floor selection inside the elevator - ent trig +get_housing_inside_trigger() +{ + return self.e[ "housing" ][ "inside_trigger" ]; +} + +// returns inner door's closing position - vec +get_housing_closedpos() +{ + return self.e[ "housing" ][ "door_closed_pos" ]; +} + +// returns inner left door entity - ent +get_housing_leftdoor() +{ + return self.e[ "housing" ][ "left_door" ]; +} + +// returns inner right door entity - ent +get_housing_rightdoor() +{ + return self.e[ "housing" ][ "right_door" ]; +} + +// returns inner left door opened position - vec +get_housing_leftdoor_opened_pos() +{ + return self.e[ "housing" ][ "left_door_opened_pos" ]; +} + +// returns inner right door opened position - vec +get_housing_rightdoor_opened_pos() +{ + return self.e[ "housing" ][ "right_door_opened_pos" ]; +} + +// floor currently the elevator is on - int +get_curFloor() +{ + moving = self elevator_floor_update(); + return self.e[ "current_floor" ]; +} + +// floor elevator initially at on level load - int +get_initFloor() +{ + return self.e[ "initial_floor" ]; +} + +waittill_finish_moving( ent1, ent1_moveto_pos, ent2, ent2_moveto_pos ) +{ + if ( !isdefined( ent2 ) && !isdefined( ent2_moveto_pos ) ) + { + ent2 = ent1; + ent2_moveto_pos = ent1_moveto_pos; + } + + while ( 1 ) + { + ent1_current_pos = ent1.origin; + etn2_current_pos = ent2.origin; + + if ( ent1_current_pos == ent1_moveto_pos && etn2_current_pos == ent2_moveto_pos ) + break; + + wait 0.05; + } +} + +// return test of min max bound on xy plane, self = entity being tested - bool +isInbound( bounding_box ) +{ + + assertex( isdefined( self ) && isdefined( self.origin ), "Fail! Can not test bounds with the entity called on" ); + + // the box isn't working on the angle that I have so I'm going to do it by a sphere shape.. -Nate + //special case cause I don't know if a sphere will break other levels. + //hey look you can make it do spheres, nobody is doing that now. + //if( level.script == "plaza" || level.script == "highrise_test" ) + //return isInBoundingSpere( bounding_box ); + + v_x = self.origin[ 0 ]; + v_y = self.origin[ 1 ]; + + min_x = bounding_box[ 0 ]; + max_x = bounding_box[ 1 ]; + min_y = bounding_box[ 2 ]; + max_y = bounding_box[ 3 ]; + + return ( v_x >= min_x && v_x <= max_x && v_y >= min_y && v_y <= max_y ); +} + +isInBoundingSpere( bounding_box ) +{ + v_x = self.origin[ 0 ]; + v_y = self.origin[ 1 ]; + min_x = bounding_box[ 0 ]; + max_x = bounding_box[ 1 ]; + min_y = bounding_box[ 2 ]; + max_y = bounding_box[ 3 ]; + + mid_x = (min_x + max_x )/2; + mid_y = (min_y + max_y )/2; + radius = abs( Distance( (min_x,min_y,0 ), (mid_x,mid_y,0)) ); + return( abs( distance( (v_x,v_y,0), (mid_x,mid_y,0) ) ) < radius ); +} + +waittill_or_timeout( msg, timer ) +{ + self endon( msg ); + wait( timer ); +} + +elevator_get_dvar_int( dvar, def ) +{ + return int( elevator_get_dvar( dvar, def ) ); +} + +elevator_get_dvar( dvar, def ) +{ + if ( getdvar( dvar ) != "" ) + return getdvarfloat( dvar ); + else + { + setdvar( dvar, def ); + return def; + } +} + +elevator_debug() +{ + if ( !level.elevator_debug ) + return; + + //print3d(, , , , , ) + + while ( 1 ) + { + // simple debug info: + + if ( level.elevator_debug != 2 ) + continue; + + // full debug info: + + // print all parts for all elevators + foreach ( i, elevator in level.elevators ) + { + mainframe = elevator get_housing_mainframe(); + musak_model = elevator get_housing_musak_model(); + + print3d( musak_model.origin, "[e" + i + "]musak_origin", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + print3d( mainframe.origin, "[e" + i + "]mainframe", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + print3d( elevator.e[ "housing" ][ "left_door" ].origin, "[e" + i + "]left door", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + print3d( elevator.e[ "housing" ][ "right_door" ].origin, "[e" + i + "]right door", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + print3d( elevator.e[ "housing" ][ "door_closed_pos" ], "[e" + i + "]->|<-", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + print3d( elevator.e[ "housing" ][ "inside_trigger" ].origin, "[e" + i + "]USE", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + + foreach ( j, eFloor in elevator.e[ "outer_doorset" ] ) + { + print3d( eFloor[ "left_door" ].origin + ( 0, 0, 8 ), "[e" + i + "][f" + j + "]left door", ( 0.75, 1.0, 0.75 ), 1, 0.25, 20 ); + print3d( eFloor[ "right_door" ].origin + ( 0, 0, 8 ), "[e" + i + "][f" + j + "]right door", ( 0.75, 1.0, 0.75 ), 1, 0.25, 20 ); + print3d( eFloor[ "door_closed_pos" ] + ( 0, 0, 8 ), "[e" + i + "][f" + j + "]->|<-", ( 0.75, 1.0, 0.75 ), 1, 0.25, 20 ); + print3d( elevator.e[ "floor" + j + "_pos" ] + ( 0, 0, 8 ), "[e" + i + "][f" + j + "]stop", ( 1.0, 0.75, 0.75 ), 1, 0.25, 20 ); + } + } + + // per button + foreach ( callbutton in level.elevator_callbuttons ) + { + print3d( callbutton.origin, "linked to:", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + + // per floor + foreach ( f_idx, eFloor in callbutton.e ) + { + // per elevator linked + printoffset = 0; + foreach ( e_idx, eLinked in eFloor ) + { + printoffset++ ; + // e_idx is used to offset print + print_pos = callbutton.origin + ( 0, 0, ( printoffset ) * ( -4 ) ); + print3d( print_pos, "[f" + f_idx + "][e" + eLinked.e[ "id" ] + "]", ( 0.75, 0.75, 1.0 ), 1, 0.25, 20 ); + } + } + } + + wait 0.05; + } +} \ No newline at end of file diff --git a/raw/common_scripts/_exploder.gsc b/raw/common_scripts/_exploder.gsc new file mode 100644 index 0000000..44deca1 --- /dev/null +++ b/raw/common_scripts/_exploder.gsc @@ -0,0 +1,1122 @@ +#include common_scripts\utility; + +/* + +Handles Old-School exploders. This is coupled with _createfx and _fx since a Radiant Created "exploder" can trigger a createfx created effect. + +This Script is legacy, tried and true, It eats up entites when using script_brushmodel swapping and a lot of script_models and the damage triggers. + +We should in a future game consider some entiity optimization in code. A way to Join models and brushes as one entity would be key. + + */ + +GetExploders( id ) +{ + id += ""; + if (isdefined(level.createFXexploders)) + return level.createFXexploders[ id ]; + + // old way + array = []; + foreach (ent in level.createFxent) + { + if ( !isdefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + if ( !isdefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] == id ) + array[array.size] = ent; + } + return array; +} + +setup_individual_exploder( ent ) +{ + exploder_num = ent.script_exploder; + if ( !IsDefined( level.exploders[ exploder_num ] ) ) + { + level.exploders[ exploder_num ] = []; + } + + targetname = ent.targetname; + if ( !IsDefined( targetname ) ) + targetname = ""; + + level.exploders[ exploder_num ][ level.exploders[ exploder_num ].size ] = ent; + if ( exploder_model_starts_hidden( ent ) ) + { + ent Hide(); + return; + } + + if ( exploder_model_is_damaged_model( ent ) ) + { + ent Hide(); + ent NotSolid(); + if ( IsDefined( ent.spawnflags ) && ( ent.spawnflags & 1 ) ) + { + if ( IsDefined( ent.script_disconnectpaths ) ) + { + ent ConnectPaths(); + } + } + return; + } + + if ( exploder_model_is_chunk( ent ) ) + { + ent Hide(); + ent NotSolid(); + if ( IsDefined( ent.spawnflags ) && ( ent.spawnflags & 1 ) ) + ent ConnectPaths(); + return; + } +} + +setupExploders() +{ + level.exploders = []; + + // Hide exploder models. + ents = GetEntArray( "script_brushmodel", "classname" ); + smodels = GetEntArray( "script_model", "classname" ); + for ( i = 0; i < smodels.size; i++ ) + ents[ ents.size ] = smodels[ i ]; + + foreach ( ent in ents ) + { + if ( IsDefined( ent.script_prefab_exploder ) ) + ent.script_exploder = ent.script_prefab_exploder; + + if ( IsDefined( ent.masked_exploder ) ) + continue; + + if ( IsDefined( ent.script_exploder ) ) + { + setup_individual_exploder( ent ); + } + } + + script_exploders = []; + + potentialExploders = GetEntArray( "script_brushmodel", "classname" ); + for ( i = 0; i < potentialExploders.size; i++ ) + { + if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) + potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; + + if ( IsDefined( potentialExploders[ i ].script_exploder ) ) + script_exploders[ script_exploders.size ] = potentialExploders[ i ]; + } + + potentialExploders = GetEntArray( "script_model", "classname" ); + for ( i = 0; i < potentialExploders.size; i++ ) + { + if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) + potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; + + if ( IsDefined( potentialExploders[ i ].script_exploder ) ) + script_exploders[ script_exploders.size ] = potentialExploders[ i ]; + } + + potentialExploders = GetEntArray( "script_origin", "classname" ); + for ( i = 0; i < potentialExploders.size; i++ ) + { + if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) + potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; + + if ( IsDefined( potentialExploders[ i ].script_exploder ) ) + script_exploders[ script_exploders.size ] = potentialExploders[ i ]; + } + + potentialExploders = GetEntArray( "item_health", "classname" ); + for ( i = 0; i < potentialExploders.size; i++ ) + { + if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) + potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; + + if ( IsDefined( potentialExploders[ i ].script_exploder ) ) + script_exploders[ script_exploders.size ] = potentialExploders[ i ]; + } + + + potentialExploders = level.struct; + for ( i = 0; i < potentialExploders.size; i++ ) + { + if ( !IsDefined( potentialExploders[ i ] ) ) + continue; // these must be getting deleted somewhere else? + if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) + potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; + + if ( IsDefined( potentialExploders[ i ].script_exploder ) ) + { + if ( !IsDefined( potentialExploders[ i ].angles ) ) + potentialExploders[ i ].angles = ( 0, 0, 0 ); + script_exploders[ script_exploders.size ] = potentialExploders[ i ]; + + } + } + + + if ( !IsDefined( level.createFXent ) ) + level.createFXent = []; + + acceptableTargetnames = []; + acceptableTargetnames[ "exploderchunk visible" ] = true; + acceptableTargetnames[ "exploderchunk" ] = true; + acceptableTargetnames[ "exploder" ] = true; + + thread setup_flag_exploders(); + + for ( i = 0; i < script_exploders.size; i++ ) + { + exploder = script_exploders[ i ]; + + + ent = createExploder( exploder.script_fxid ); + ent.v = []; + ent.v[ "origin" ] = exploder.origin; + ent.v[ "angles" ] = exploder.angles; + ent.v[ "delay" ] = exploder.script_delay; + ent.v[ "delay_post" ] = exploder.script_delay_post; + ent.v[ "firefx" ] = exploder.script_firefx; + ent.v[ "firefxdelay" ] = exploder.script_firefxdelay; + ent.v[ "firefxsound" ] = exploder.script_firefxsound; + ent.v[ "firefxtimeout" ] = exploder.script_firefxtimeout; + ent.v[ "earthquake" ] = exploder.script_earthquake; + ent.v[ "rumble" ] = exploder.script_rumble; + ent.v[ "damage" ] = exploder.script_damage; + ent.v[ "damage_radius" ] = exploder.script_radius; + ent.v[ "soundalias" ] = exploder.script_soundalias; + ent.v[ "repeat" ] = exploder.script_repeat; + ent.v[ "delay_min" ] = exploder.script_delay_min; + ent.v[ "delay_max" ] = exploder.script_delay_max; + ent.v[ "target" ] = exploder.target; + ent.v[ "ender" ] = exploder.script_ender; + ent.v[ "physics" ] = exploder.script_physics; + ent.v[ "type" ] = "exploder"; +// ent.v[ "worldfx" ] = true; + if ( !IsDefined( exploder.script_fxid ) ) + ent.v[ "fxid" ] = "No FX"; + else + ent.v[ "fxid" ] = exploder.script_fxid; + ent.v [ "exploder" ] = exploder.script_exploder; + AssertEx( IsDefined( exploder.script_exploder ), "Exploder at origin " + exploder.origin + " has no script_exploder" ); + if ( IsDefined( level.createFXexploders ) ) + { // if we're using the optimized lookup, add it in the proper place + ary = level.createFXexploders[ ent.v[ "exploder" ] ]; + if ( !IsDefined( ary ) ) + ary = []; + ary[ ary.size ] = ent; + level.createFXexploders[ ent.v[ "exploder" ] ] = ary; + } + + if ( !IsDefined( ent.v[ "delay" ] ) ) + ent.v[ "delay" ] = 0; + + if ( IsDefined( exploder.target ) ) + { + get_ent = GetEntArray( ent.v[ "target" ], "targetname" )[ 0 ]; + if ( IsDefined( get_ent ) ) + { + org = get_ent.origin; + ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] ); + } + else + { + get_ent = get_target_ent( ent.v[ "target" ] ); + if ( IsDefined( get_ent ) ) + { + org = get_ent.origin; + ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] ); + } + } + } + + // this basically determines if its a brush / model exploder or not + if ( !IsDefined( exploder.code_classname ) ) + { + //I assume everything that doesn't have a code_classname is a struct that needs a script_modelname to make its way into the game + ent.model = exploder; + if ( IsDefined( ent.model.script_modelname ) ) + { + PreCacheModel( ent.model.script_modelname ); + } + } + else if ( exploder.code_classname == "script_brushmodel" || IsDefined( exploder.model ) ) + { + ent.model = exploder; + ent.model.disconnect_paths = exploder.script_disconnectpaths; + } + + if ( IsDefined( exploder.targetname ) && IsDefined( acceptableTargetnames[ exploder.targetname ] ) ) + ent.v[ "exploder_type" ] = exploder.targetname; + else + ent.v[ "exploder_type" ] = "normal"; + + if ( IsDefined( exploder.masked_exploder ) ) + { + ent.v[ "masked_exploder" ] = exploder.model; + ent.v[ "masked_exploder_spawnflags" ] = exploder.spawnflags; + ent.v[ "masked_exploder_script_disconnectpaths" ] = exploder.script_disconnectpaths; + exploder Delete(); + } + ent common_scripts\_createfx::post_entity_creation_function(); + } +} + +setup_flag_exploders() +{ + // createfx has to do 2 waittillframeends so we have to do 3 to make sure this comes after + // createfx is all done setting up. Who will raise the gambit to 4? + waittillframeend; + waittillframeend; + waittillframeend; + exploder_flags = []; + + foreach ( ent in level.createFXent ) + { + if ( ent.v[ "type" ] != "exploder" ) + continue; + theFlag = ent.v[ "flag" ]; + + if ( !IsDefined( theFlag ) ) + { + continue; + } + + if ( theFlag == "nil" ) + { + ent.v[ "flag" ] = undefined; + } + + exploder_flags[ theFlag ] = true; + } + + foreach ( msg, _ in exploder_flags ) + { + thread exploder_flag_wait( msg ); + } +} + +exploder_flag_wait( msg ) +{ + if ( !flag_exist( msg ) ) + flag_init( msg ); + flag_wait( msg ); + + foreach ( ent in level.createFXent ) + { + if ( ent.v[ "type" ] != "exploder" ) + continue; + theFlag = ent.v[ "flag" ]; + + if ( !IsDefined( theFlag ) ) + { + continue; + } + + if ( theFlag != msg ) + continue; + ent activate_individual_exploder(); + } +} + +exploder_model_is_damaged_model( ent ) +{ + return( IsDefined( ent.targetname ) ) && ( ent.targetname == "exploder" ); +} + +exploder_model_starts_hidden( ent ) +{ + return( ent.model == "fx" ) && ( ( !IsDefined( ent.targetname ) ) || ( ent.targetname != "exploderchunk" ) ); +} + +exploder_model_is_chunk( ent ) +{ + return( IsDefined( ent.targetname ) ) && ( ent.targetname == "exploderchunk" ); +} + +show_exploder_models_proc( num ) +{ + num += ""; + + //prof_begin( "hide_exploder" ); + if ( IsDefined( level.createFXexploders ) ) + { // do optimized flavor if available + exploders = level.createFXexploders[ num ]; + if ( IsDefined( exploders ) ) + { + foreach ( ent in exploders ) + { + //pre exploded geo. don't worry about deleted exploder geo.. + if ( ! exploder_model_starts_hidden( ent.model ) + && ! exploder_model_is_damaged_model( ent.model ) + && !exploder_model_is_chunk( ent.model ) ) + { + ent.model Show(); + } + + //exploded geo and should be shown + if ( IsDefined( ent.brush_shown ) ) + ent.model Show(); + } + } + } + else + { + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( !IsDefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != num ) + continue; + + if ( IsDefined( ent.model ) ) + { + + //pre exploded geo. don't worry about deleted exploder geo.. + if ( ! exploder_model_starts_hidden( ent.model ) && ! exploder_model_is_damaged_model( ent.model ) && !exploder_model_is_chunk( ent.model ) ) + { + ent.model Show(); + } + + //exploded geo and should be shown + if ( IsDefined( ent.brush_shown ) ) + ent.model Show(); + + } + } + } + //prof_end( "hide_exploder" ); +} + +stop_exploder_proc( num ) +{ + num += ""; + + if ( IsDefined( level.createFXexploders ) ) + { // do optimized flavor if available + exploders = level.createFXexploders[ num ]; + if ( IsDefined( exploders ) ) + { + foreach ( ent in exploders ) + { + if ( !IsDefined( ent.looper ) ) + continue; + + ent.looper Delete(); + } + } + } + else + { + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( !IsDefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != num ) + continue; + + if ( !IsDefined( ent.looper ) ) + continue; + + ent.looper Delete(); + } + } +} + +get_exploder_array_proc( msg ) +{ + msg += ""; + array = []; + if ( IsDefined( level.createFXexploders ) ) + { // do optimized flavor if available + exploders = level.createFXexploders[ msg ]; + if ( IsDefined( exploders ) ) + { + array = exploders; + } + } + else + { + foreach ( ent in level.createFXent ) + { + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != msg ) + continue; + + array[ array.size ] = ent; + } + } + return array; +} + +hide_exploder_models_proc( num ) +{ + num += ""; + + //prof_begin( "hide_exploder" ); + + if ( IsDefined( level.createFXexploders ) ) + { // do optimized flavor if available + exploders = level.createFXexploders[ num ]; + if ( IsDefined( exploders ) ) + { + foreach ( ent in exploders ) + { + if ( IsDefined( ent.model ) ) + ent.model Hide(); + } + } + } + else + { + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( !IsDefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != num ) + continue; + + + if ( IsDefined( ent.model ) ) + ent.model Hide(); + + } + } + //prof_end( "hide_exploder" ); +} + +delete_exploder_proc( num ) +{ + num += ""; + + //prof_begin( "delete_exploder" ); + if ( IsDefined( level.createFXexploders ) ) + { // do optimized flavor if available + exploders = level.createFXexploders[ num ]; + if ( IsDefined( exploders ) ) + { + foreach ( ent in exploders ) + { + if ( IsDefined( ent.model ) ) + ent.model Delete(); + } + } + } + else + { + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + if ( !IsDefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != num ) + continue; + + if ( IsDefined( ent.model ) ) + ent.model Delete(); + } + } + //ends trigger threads. + level notify( "killexplodertridgers" + num ); + + //prof_end( "delete_exploder" ); +} + +exploder_damage() +{ + if ( IsDefined( self.v[ "delay" ] ) ) + delay = self.v[ "delay" ]; + else + delay = 0; + + if ( IsDefined( self.v[ "damage_radius" ] ) ) + radius = self.v[ "damage_radius" ]; + else + radius = 128; + + damage = self.v[ "damage" ]; + origin = self.v[ "origin" ]; + + wait( delay ); + + if ( IsDefined( level.custom_radius_damage_for_exploders ) ) + [[ level.custom_radius_damage_for_exploders ]]( origin, radius, damage ); + else + // Range, max damage, min damage + RadiusDamage( origin, radius, damage, damage ); +} + +activate_individual_exploder_proc() +{ + if ( IsDefined( self.v[ "firefx" ] ) ) + self thread fire_effect(); + + if ( IsDefined( self.v[ "fxid" ] ) && self.v[ "fxid" ] != "No FX" ) + self thread cannon_effect(); + else + if ( IsDefined( self.v[ "soundalias" ] ) && self.v[ "soundalias" ] != "nil" ) + self thread sound_effect(); + + if ( IsDefined( self.v[ "loopsound" ] ) && self.v[ "loopsound" ] != "nil" ) + self thread effect_loopsound(); + + if ( IsDefined( self.v[ "damage" ] ) ) + self thread exploder_damage(); + + if ( IsDefined( self.v[ "earthquake" ] ) ) + self thread exploder_earthquake(); + + if ( IsDefined( self.v[ "rumble" ] ) ) + self thread exploder_rumble(); + + if ( self.v[ "exploder_type" ] == "exploder" ) + self thread brush_show(); + else + if ( ( self.v[ "exploder_type" ] == "exploderchunk" ) || ( self.v[ "exploder_type" ] == "exploderchunk visible" ) ) + self thread brush_throw(); + else + self thread brush_delete(); +} + +brush_delete() +{ +// if( ent.v[ "exploder_type" ] != "normal" && !isdefined( ent.v[ "fxid" ] ) && !isdefined( ent.v[ "soundalias" ] ) ) +// if( !isdefined( ent.script_fxid ) ) + + num = self.v[ "exploder" ]; + if ( IsDefined( self.v[ "delay" ]) && (self.v[ "delay" ] >= 0) ) + wait( self.v[ "delay" ] ); + else + wait( 0.05 ); // so it disappears after the replacement appears + + if ( !IsDefined( self.model ) ) + return; + + + Assert( IsDefined( self.model ) ); + + if ( IsDefined( self.model.classname ) ) // script_struct support - Nate -- self is always a struct so it will never have a classname, using self.model instead -Carlos + if ( isSP() && ( self.model.spawnflags & 1 ) ) + self.model call [[ level.connectPathsFunction ]](); + + if ( level.createFX_enabled ) + { + if ( IsDefined( self.exploded ) ) + return; + + self.exploded = true; + self.model Hide(); + self.model NotSolid(); + + wait( 3 ); + self.exploded = undefined; + self.model Show(); + self.model Solid(); + return; + } + + if ( !IsDefined( self.v[ "fxid" ] ) || self.v[ "fxid" ] == "No FX" ) + self.v[ "exploder" ] = undefined; + + waittillframeend;// so it hides stuff after it shows the new stuff + +// if ( IsDefined( self.classname ) ) // script_struct support nate --- self is always a struct so it will never have a classname, using self.model instead -Carlos + if ( IsDefined( self.model ) && IsDefined( self.model.classname ) ) + { + self.model Delete(); + } +} + +brush_throw() +{ + if ( IsDefined( self.v[ "delay" ] ) ) + wait( self.v[ "delay" ] ); + + ent = undefined; + if ( IsDefined( self.v[ "target" ] ) ) + ent = get_target_ent( self.v[ "target" ] ); + + if ( !IsDefined( ent ) ) + { + self.model Delete(); + return; + } + + self.model Show(); + + if ( IsDefined( self.v[ "delay_post" ] ) ) + wait( self.v[ "delay_post" ] ); + + startorg = self.v[ "origin" ]; + startang = self.v[ "angles" ]; + org = ent.origin; + + temp_vec = ( org - self.v[ "origin" ] ); + x = temp_vec[ 0 ]; + y = temp_vec[ 1 ]; + z = temp_vec[ 2 ]; + + physics = IsDefined( self.v[ "physics" ] ); + if ( physics ) + { + target = undefined; + if ( IsDefined( ent.target ) ) + target = ent get_target_ent(); + + if ( !IsDefined( target ) ) + { + contact_point = startorg; // no spin just push it. + throw_vec = ent.origin; + } + else + { + contact_point = ent.origin; + throw_vec = ( ( target.origin - ent.origin ) * self.v[ "physics" ] ); + + } + self.model PhysicsLaunchClient( contact_point, throw_vec ); + return; + } + else + { + self.model RotateVelocity( ( x, y, z ), 12 ); + self.model MoveGravity( ( x, y, z ), 12 ); + } + + if ( level.createFX_enabled ) + { + if ( IsDefined( self.exploded ) ) + return; + + self.exploded = true; + wait( 3 ); + self.exploded = undefined; + self.v[ "origin" ] = startorg; + self.v[ "angles" ] = startang; + self.model Hide(); + return; + } + + self.v[ "exploder" ] = undefined; + wait( 6 ); + self.model Delete(); +} + +brush_show() +{ + if ( IsDefined( self.v[ "delay" ] ) ) + wait( self.v[ "delay" ] ); + + Assert( IsDefined( self.model ) ); + + if ( !IsDefined( self.model.script_modelname ) ) + { + self.model Show(); + self.model Solid(); + } + else // script_structs don't pass in their .model value SO use script_modelname on them saves an entity. + { + model = self.model spawn_tag_origin(); + if ( IsDefined( self.model.script_linkname ) ) + model.script_linkname = self.model.script_linkname; + model SetModel( self.model.script_modelname ); + model Show(); + } + + self.brush_shown = true; // used for hiding an exploder. + + if ( isSP() && !IsDefined( self.model.script_modelname ) && ( self.model.spawnflags & 1 ) ) + { + if ( !IsDefined( self.model.disconnect_paths ) ) + self.model call [[ level.connectPathsFunction ]](); + else + self.model call [[ level.disconnectPathsFunction ]](); + } + + if ( level.createFX_enabled ) + { + if ( IsDefined( self.exploded ) ) + return; + + self.exploded = true; + wait( 3 ); + self.exploded = undefined; + + if ( !IsDefined( self.model.script_modelname ) ) + { + self.model Hide(); + self.model NotSolid(); + } + } +} + +exploder_rumble() +{ + if ( !isSP() ) + return; + + self exploder_delay(); + level.player PlayRumbleOnEntity( self.v[ "rumble" ] ); +} + +exploder_delay() +{ + if ( !IsDefined( self.v[ "delay" ] ) ) + self.v[ "delay" ] = 0; + + min_delay = self.v[ "delay" ]; + max_delay = self.v[ "delay" ] + 0.001; // cant randomfloatrange on the same # + if ( IsDefined( self.v[ "delay_min" ] ) ) + min_delay = self.v[ "delay_min" ]; + + if ( IsDefined( self.v[ "delay_max" ] ) ) + max_delay = self.v[ "delay_max" ]; + + if ( min_delay > 0 ) + wait( RandomFloatRange( min_delay, max_delay ) ); +} + +effect_loopsound() +{ + if ( IsDefined( self.loopsound_ent ) ) + { + self.loopsound_ent Delete(); + } + // save off this info in case we delete the effect + origin = self.v[ "origin" ]; + alias = self.v[ "loopsound" ]; + self exploder_delay(); + + self.loopsound_ent = play_loopsound_in_space( alias, origin ); +} + +sound_effect() +{ + self effect_soundalias(); +} + +effect_soundalias() +{ + // save off this info in case we delete the effect + origin = self.v[ "origin" ]; + alias = self.v[ "soundalias" ]; + self exploder_delay(); + play_sound_in_space( alias, origin ); +} + +exploder_earthquake() +{ + self exploder_delay(); + do_earthquake( self.v[ "earthquake" ], self.v[ "origin" ] ); +} + +exploder_playSound() +{ + if ( !IsDefined( self.v[ "soundalias" ] ) || self.v[ "soundalias" ] == "nil" ) + return; + + play_sound_in_space( self.v[ "soundalias" ], self.v[ "origin" ] ); +} + +fire_effect() +{ + forward = self.v[ "forward" ]; + up = self.v[ "up" ]; + + org = undefined; + + firefxSound = self.v[ "firefxsound" ]; + origin = self.v[ "origin" ]; + firefx = self.v[ "firefx" ]; + ender = self.v[ "ender" ]; + if ( !IsDefined( ender ) ) + ender = "createfx_effectStopper"; + + fireFxDelay = 0.5; + if ( IsDefined( self.v[ "firefxdelay" ] ) ) + fireFxDelay = self.v[ "firefxdelay" ]; + + self exploder_delay(); + + if ( IsDefined( firefxSound ) ) + loop_fx_sound_with_angles( firefxSound, origin, (0, 0, 0), 1, ender ); + + PlayFX( level._effect[ firefx ], self.v[ "origin" ], forward, up ); +} + +cannon_effect() +{ + if ( IsDefined( self.v[ "repeat" ] ) ) + { + thread exploder_playSound(); + for ( i = 0; i < self.v[ "repeat" ]; i++ ) + { + PlayFX( level._effect[ self.v[ "fxid" ] ], self.v[ "origin" ], self.v[ "forward" ], self.v[ "up" ] ); + self exploder_delay(); + } + return; + } + if ( !isdefined( self.v[ "delay" ] ) ) + self.v[ "delay" ] = 0; + if(self.v[ "delay" ] >= 0) + { + self exploder_delay(); + preroll = 0; + } + else + preroll = self.v[ "delay" ]; + + if ( IsDefined( self.looper ) ) + self.looper Delete(); + + self.looper = SpawnFx( getfx( self.v[ "fxid" ] ), self.v[ "origin" ], self.v[ "forward" ], self.v[ "up" ] ); + if ( level.createFX_enabled ) + SetFXKillOnDelete(self.looper,true); + if(self.v[ "delay" ] >= 0) + TriggerFX( self.looper ); + else + TriggerFX( self.looper, preroll ); + exploder_playSound(); +} + +activate_exploder( num, players, startTime ) +{ + num += ""; + //prof_begin( "activate_exploder" ); + + //here's a hook so you can know when a certain number of an exploder is going off + level notify( "exploding_" + num ); + + found_server_exploder = false; + + if ( IsDefined( level.createFXexploders ) && !level.createFX_enabled ) + { // do optimized form if available + exploders = level.createFXexploders[ num ]; + if ( IsDefined( exploders ) ) + { + foreach ( ent in exploders ) + { + if ( !ent check_exploder_platform() ) + continue; + ent activate_individual_exploder(); + found_server_exploder = true; + } + } + } + else + { + for ( i = 0;i < level.createFXent.size;i++ ) + { + ent = level.createFXent[ i ]; + if ( !IsDefined( ent ) ) + continue; + + if ( ent.v[ "type" ] != "exploder" ) + continue; + + // make the exploder actually removed the array instead? + if ( !IsDefined( ent.v[ "exploder" ] ) ) + continue; + + if ( ent.v[ "exploder" ] + "" != num ) + continue; + + if ( !ent check_exploder_platform() ) + continue; + + ent activate_individual_exploder(); + found_server_exploder = true; + } + } + + if ( !shouldRunServerSideEffects() && !found_server_exploder ) + activate_clientside_exploder( num, players, startTime ); + + //prof_end( "activate_exploder" ); +} + + +/* +============= +///ScriptDocBegin +"Name: exploder( , , )" +"Summary: Sets off the desired exploder" +"Module: Utility" +"MandatoryArg: : The exploder number" +"OptionalArg: : (MP_ONLY) Players to activate exploder for. Defaults to all players." +"OptionalArg: : (MP_ONLY) The time at which the exploder should be triggered. This can be in the past." +"Example: exploder( 5 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +exploder( num, players, startTime ) +{ + [[ level._fx.exploderFunction ]]( num, players, startTime ); +} + + +/* +============= +///ScriptDocBegin +"Name: kill_exploder( )" +"Summary: Hard kills the desired exploder" +"Module: FX" +"MandatoryArg: : The exploder number" +"Example: kill_exploder( 5 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +kill_exploder( num ) +{ + exploders = getexploders( num ); + + if (isdefined(exploders)) + { + foreach (ent in exploders) + { + if ( IsDefined( ent.looper ) ) + { + SetFXKillOnDelete(ent.looper, true); + } + } + waitframe(); + foreach (ent in exploders) + { + ent pauseEffect(); + } + } +} + +check_exploder_platform() +{ + ent = self; + if ( IsDefined( ent.v[ "platform" ] ) && IsDefined( level.currentgen ) ) + { + platform = ent.v[ "platform" ]; + if ( ( platform == "cg" && !level.currentgen ) || + ( platform == "ng" && !level.nextgen ) ) + { + return false; + } + } + return true; +} + +activate_clientside_exploder( exploderName, players, startTime ) +{ + if ( !is_valid_clientside_exploder_name( exploderName ) ) + { + PrintLn( "^1ERROR: Exploder Index '" + exploderName + "' is not a valid exploder index >= 0" ); + return; + } + + exploder_num = Int( exploderName ); + ActivateClientExploder( exploder_num, players, startTime ); +} + +deactivate_clientside_exploder( exploderName, players, shouldKill ) +{ + if ( !is_valid_clientside_exploder_name( exploderName ) ) + { + PrintLn( "^1ERROR: Exploder Index '" + exploderName + "' is not a valid exploder index >= 0" ); + return; + } + + exploder_num = Int( exploderName ); + StopClientExploder( exploder_num, players, shouldKill ); +} + +is_valid_clientside_exploder_name( exploderName ) +{ + if ( !IsDefined( exploderName ) ) + return false; + + exploder_num = exploderName; + if ( IsString( exploderName ) ) + { + exploder_num = Int( exploderName ); + if ( exploder_num == 0 && exploderName != "0" ) + return false; + } + + return exploder_num >= 0; +} + +shouldRunServerSideEffects() +{ + if ( isSP() ) + return true; + + if ( !IsDefined( level.createFX_enabled ) ) + level.createFX_enabled = ( GetDvar( "createfx" ) != "" ); + + if ( level.createFX_enabled ) + return true; + else + return GetDvar( "clientSideEffects" ) != "1"; +} + +exploder_before_load( num, players, startTime ) +{ + // gotta wait twice because the createfx_init function waits once then inits all exploders. This guarentees + // that if an exploder is run on the first frame, it happens after the fx are init. + waittillframeend; + waittillframeend; + activate_exploder( num, players, startTime ); +} + +exploder_after_load( num, players, startTime ) +{ + activate_exploder( num, players, startTime ); +} \ No newline at end of file diff --git a/raw/common_scripts/_fx.gsc b/raw/common_scripts/_fx.gsc new file mode 100644 index 0000000..c214b2d --- /dev/null +++ b/raw/common_scripts/_fx.gsc @@ -0,0 +1,970 @@ +#include common_scripts\utility; +#include common_scripts\_createfx; +//#include soundscripts\_audio; +//#include soundscripts\_audio_dynamic_ambi; + + +CONST_MAX_SP_CREATEFX = 1500; +CONST_MAX_SP_CREATESOUND = 384; +initFX() +{ + if ( !isdefined( level.func ) ) + level.func = []; + + if ( !isdefined( level.func[ "create_triggerfx" ] ) ) + level.func[ "create_triggerfx" ] = ::create_triggerfx; + + if ( !IsDefined( level._fx ) ) + level._fx = SpawnStruct(); + + create_lock( "createfx_looper", 20 ); + level.fxfireloopmod = 1; + + // wrapper for the exploder function so we dont have to use flags and do ifs/waittills on every exploder call + level._fx.exploderFunction = common_scripts\_exploder::exploder_before_load; + waittillframeend;// Wait one frame so the effects get setup by the maps fx thread + waittillframeend;// Wait another frame so effects can be loaded based on start functions. Without this FX are initialiazed before they are defined by start functions. + level._fx.exploderFunction = common_scripts\_exploder::exploder_after_load; + + level._fx.server_culled_sounds = false; + if ( GetDvarInt( "serverCulledSounds" ) == 1 ) + level._fx.server_culled_sounds = true; + + if ( level.createFX_enabled ) + level._fx.server_culled_sounds = false; + +/# + SetDevDvarIfUninitialized( "scr_map_exploder_dump", 0 ); + SetDevDvarIfUninitialized( "createfx_removedupes", 0 ); + + if ( GetDvarInt( "r_reflectionProbeGenerate" ) == 1 ) + level._fx.server_culled_sounds = true; +#/ + + // Give createfx_common time to delete triggers to free up entity slots. + if ( level.createFX_enabled ) + { + level waittill( "createfx_common_done" ); + } + +/# + remove_dupes(); +#/ + + for ( i = 0; i < level.createFXent.size; i++ ) + { + ent = level.createFXent[ i ]; + ent set_forward_and_up_vectors(); + + switch ( ent.v[ "type" ] ) + { + case "loopfx": + ent thread loopfxthread(); + break; + case "oneshotfx": + ent thread oneshotfxthread(); + break; + case "soundfx": + ent thread create_loopsound(); + break; + case "soundfx_interval": + ent thread create_interval_sound(); + break; + case "reactive_fx": + ent add_reactive_fx(); + break; + case "soundfx_dynamic": + ent thread create_dynamicambience(); + break; + } + } + + check_createfx_limit(); +} + +remove_dupes() +{ +/# + if ( GetDvarInt( "createfx_removedupes" ) == 0 ) + return; + + new_ents = []; + for ( i = 0; i < level.createFXent.size; i++ ) + { + add_ent = true; + i_ent = level.createFXent[ i ]; + for ( j = i + 1; j < level.createFXent.size; j++ ) + { + j_ent = level.createFXent[ j ]; + + if ( j_ent.v[ "type" ] == i_ent.v[ "type" ] ) + { + if ( j_ent.v[ "origin" ] == i_ent.v[ "origin" ] ) + { + println( "^3--REMOVING DUPE'D " + j_ent.v[ "type" ] + " AT " + j_ent.v[ "origin" ] ); + add_ent = false; + } + } + } + + if ( add_ent ) + new_ents[ new_ents.size ] = i_ent; + } + + level.createFXent = new_ents; +#/ +} + +check_createfx_limit() +{ +/# + if ( !isSP() ) + { + return; + } + + fx_count = 0; + sound_count = 0; + foreach ( ent in level.createFXent ) + { + if ( is_createfx_type( ent, "fx" ) ) + fx_count++; + else if ( is_createfx_type( ent, "sound" ) ) + sound_count++; + } + + println( "^5Total CreateFX FX Ents: " + fx_count ); + println( "^5Total CreateFX SOUND Ents: " + sound_count ); + + check_limit_type( "fx", fx_count ); + check_limit_type( "sound", sound_count ); +#/ +} + +check_limit_type( type, count ) +{ +/# + limit = undefined; + if ( type == "fx" ) + { + limit = CONST_MAX_SP_CREATEFX; + } + else if ( type == "sound" ) + { + limit = CONST_MAX_SP_CREATESOUND; + } + + if ( count > limit ) + AssertMsg( "CREATEFX: You have too many " + type + " createFX ents. You need to reduce the amount.\nYou have " + count + " and the limit is " + limit ); +#/ +} + + +print_org( fxcommand, fxId, fxPos, waittime ) +{ + if ( GetDvar( "debug" ) == "1" ) + { + println( "{" ); + println( "\"origin\" \"" + fxPos[ 0 ] + " " + fxPos[ 1 ] + " " + fxPos[ 2 ] + "\"" ); + println( "\"classname\" \"script_model\"" ); + println( "\"model\" \"fx\"" ); + println( "\"script_fxcommand\" \"" + fxcommand + "\"" ); + println( "\"script_fxid\" \"" + fxId + "\"" ); + println( "\"script_delay\" \"" + waittime + "\"" ); + println( "}" ); + } +} + +PlatformMatches() +{ + if ( IsDefined( self.v[ "platform" ] ) && IsDefined( level.currentgen ) ) + { + platform = self.v[ "platform" ]; + + if (( platform == "cg" && !level.currentgen ) || + ( platform == "ng" && !level.nextgen ) || + ( platform == "xenon" && !level.xenon ) || + ( platform == "ps3" && !level.ps3 ) || + ( platform == "pc" && !level.pc ) || + ( platform == "xb3" && !level.xb3 ) || + ( platform == "ps4" && !level.ps4 ) || + ( platform == "pccg" && !level.pccg ) || + ( platform == "!cg" && level.currentgen ) || + ( platform == "!ng" && level.nextgen ) || + ( platform == "!xenon" && level.xenon ) || + ( platform == "!ps3" && level.ps3 ) || + ( platform == "!pc" && level.pc ) || + ( platform == "!xb3" && level.xb3 ) || + ( platform == "!ps4" && level.ps4 ) || + ( platform == "!pccg" && level.pccg ) ) + { + return false; + } + } + + return true; +} + +OneShotfx( fxId, fxPos, waittime, fxPos2 ) +{ +// level thread print_org ("OneShotfx", fxId, fxPos, waittime); +// level thread OneShotfxthread (fxId, fxPos, waittime, fxPos2); +} + +exploderfx( num, fxId, fxPos, waittime, fxPos2, fireFx, fireFxDelay, fireFxSound, fxSound, fxQuake, fxDamage, soundalias, repeat, delay_min, delay_max, damage_radius, fireFxTimeout, exploder_group ) +{ + if ( 1 ) + { + ent = createExploder( fxId ); + ent.v[ "origin" ] = fxPos; + ent.v[ "angles" ] = ( 0, 0, 0 ); + if ( isdefined( fxPos2 ) ) + ent.v[ "angles" ] = vectortoangles( fxPos2 - fxPos ); + ent.v[ "delay" ] = waittime; + ent.v[ "exploder" ] = num; + if (isdefined(level.createFXexploders)) + { // if we're using the optimized lookup, add it in the proper place + ary = level.createFXexploders[ ent.v[ "exploder" ] ]; + if (!isdefined(ary)) + ary = []; + ary[ary.size] = ent; + level.createFXexploders[ ent.v[ "exploder" ] ] = ary; + } + // deprecated + return; + } + fx = spawn( "script_origin", ( 0, 0, 0 ) ); +// println ("total ", getentarray ("script_origin","classname").size); + fx.origin = fxPos; + fx.angles = vectortoangles( fxPos2 - fxPos ); +// fx.targetname = "exploder"; + fx.script_exploder = num; + fx.script_fxid = fxId; + fx.script_delay = waittime; + + fx.script_firefx = fireFx; + fx.script_firefxdelay = ( fireFxDelay );// for awhile the script exported strings for this value so we cast it to float + fx.script_firefxsound = fireFxSound; + + fx.script_sound = fxSound; + fx.script_earthquake = fxQuake; + fx.script_damage = ( fxDamage ); + fx.script_radius = ( damage_radius ); + fx.script_soundalias = soundalias; + fx.script_firefxtimeout = ( fireFxTimeout ); + fx.script_repeat = ( repeat ); + fx.script_delay_min = ( delay_min ); + fx.script_delay_max = ( delay_max ); + fx.script_exploder_group = exploder_group; + + forward = anglestoforward( fx.angles ); + forward *= ( 150 ); + fx.targetPos = fxPos + forward; + + if ( !isdefined( level._script_exploders ) ) + level._script_exploders = []; + level._script_exploders[ level._script_exploders.size ] = fx; +} + + +/* +loopfxRotate(fxId, fxPos, waittime, angle, fxStart, fxStop, timeout) +{ + level thread print_org ("loopfx", fxId, fxPos, waittime); + level thread loopfxthread (fxId, fxPos, waittime, fxPos2, fxStart, fxStop, timeout); +} +*/ + + +loopfx( fxId, fxPos, waittime, fxPos2, fxStart, fxStop, timeout ) +{ + println( "Loopfx is deprecated!" ); + ent = createLoopEffect( fxId ); + ent.v[ "origin" ] = fxPos; + ent.v[ "angles" ] = ( 0, 0, 0 ); + if ( isdefined( fxPos2 ) ) + ent.v[ "angles" ] = vectortoangles( fxPos2 - fxPos ); + ent.v[ "delay" ] = waittime; +} + +/* +loopfx(fxId, fxPos, waittime, fxPos2, fxStart, fxStop, timeout) +{ + level thread print_org ("loopfx", fxId, fxPos, waittime); + level thread loopfxthread (fxId, fxPos, waittime, fxPos2, fxStart, fxStop, timeout); +} +*/ + +create_looper() +{ + //assert (isdefined(self.looper)); + self.looper = playLoopedFx( level._effect[ self.v[ "fxid" ] ], self.v[ "delay" ], self.v[ "origin" ], 0, self.v[ "forward" ], self.v[ "up" ] ); + create_loopsound(); +} + +create_loopsound() +{ + if ( !PlatformMatches() ) + { + return; + } + + self notify( "stop_loop" ); + + if ( !IsDefined( self.v[ "soundalias" ] ) ) + return; + + if ( self.v[ "soundalias" ] == "nil" ) + return; + + /# + if ( GetDvar( "r_reflectionProbeGenerate" ) == "1" ) + return; + #/ + + culled = false; + end_on = undefined; + if ( isdefined( self.v[ "stopable" ] ) && self.v[ "stopable" ] ) + { + if ( IsDefined( self.looper ) ) + end_on = "death"; + else + end_on = "stop_loop"; + } + else + { + if ( level._fx.server_culled_sounds && IsDefined( self.v[ "server_culled" ] ) ) + culled = self.v[ "server_culled" ]; + } + + ent = self; + if ( IsDefined( self.looper ) ) + ent = self.looper; + + createfx_ent = undefined; + if ( level.createFX_enabled ) + createfx_ent = self; + + ent loop_fx_sound_with_angles( self.v[ "soundalias" ], self.v[ "origin" ], self.v[ "angles" ], culled, end_on, createfx_ent ); +} + +create_interval_sound() +{ + if ( !PlatformMatches() ) + { + return; + } + + self notify( "stop_loop" ); + + if ( !IsDefined( self.v[ "soundalias" ] ) ) + return; + if ( self.v[ "soundalias" ] == "nil" ) + return; + + ender = undefined; + runner = self; + +/# + if ( GetDvar( "r_reflectionProbeGenerate" ) == "1" ) + return; +#/ + + + if( ( IsDefined( self.v[ "stopable" ] ) && self.v[ "stopable" ] ) || level.createFX_enabled ) + { + if ( IsDefined( self.looper ) ) + { + runner = self.looper; + ender = "death"; + } + else + ender = "stop_loop"; + + } + + runner thread loop_fx_sound_interval_with_angles( self.v[ "soundalias" ], self.v[ "origin" ], self.v[ "angles" ], ender, undefined, self.v[ "delay_min" ], self.v[ "delay_max" ] ); +} + +create_dynamicambience() +{ + if ( !PlatformMatches() ) + { + return; + } + + if ( !IsDefined( self.v[ "ambiencename" ] ) ) + return; + if ( self.v[ "ambiencename" ] == "nil" ) + return; + + if ( isSP() ) + { + // TODO - Make this work in SP + /* + if ( IsDefined( self.dambInfoStruct ) ) + { + DAMB_stop_preset_at_point( self.v[ "ambiencename" ], self.dambInfoStruct.unique_id ); + } + + DAMB_start_preset_at_point( self.v[ "ambiencename" ], self.v[ "origin" ], undefined, self.v[ "dynamic_distance" ], self.dambInfoStruct.unique_id ); + */ + } + else + { + if ( GetDvar( "createfx" ) == "on" ) + { + // wait till the player spawns and createfxlogic starts before continuing. + flag_wait( "createfx_started" ); + } + + if ( IsDefined( self.dambInfoStruct ) ) + { + level.player StopDynamicAmbience( self.dambInfoStruct.unique_id ); + } + + self.dambInfoStruct = SpawnStruct(); + self.dambInfoStruct assign_unique_id(); + level.player PlayDynamicAmbience( self.v[ "ambiencename" ], self.v[ "origin" ], self.v[ "dynamic_distance" ], self.dambInfoStruct.unique_id ); + } +} + +loopfxthread() +{ + waitframe(); +// println ( "fx testing running Id: ", fxId ); +// if ((isdefined (level.scr_sound)) && (isdefined (level.scr_sound[fxId]))) +// loopSound(level.scr_sound[fxId], fxPos); + + if ( isdefined( self.fxStart ) ) + level waittill( "start fx" + self.fxStart ); + + while ( 1 ) + { + /* + if (isdefined (ent.org2)) + { + fxAngle = vectorNormalize (ent.org2 - ent.org); + looper = playLoopedFx( level._effect[fxId], ent.delay, ent.org, 0, fxAngle ); + } + else + looper = playLoopedFx( level._effect[fxId], ent.delay, ent.org, 0 ); + */ + create_looper(); + + if ( isdefined( self.timeout ) ) + thread loopfxStop( self.timeout ); + + if ( isdefined( self.fxStop ) ) + level waittill( "stop fx" + self.fxStop ); + else + return; + + if ( isdefined( self.looper ) ) + self.looper delete(); + + if ( isdefined( self.fxStart ) ) + level waittill( "start fx" + self.fxStart ); + else + return; + } +} + +loopfxChangeID( ent ) +{ + self endon( "death" ); + ent waittill( "effect id changed", change ); +} + +loopfxChangeOrg( ent ) +{ + self endon( "death" ); + for ( ;; ) + { + ent waittill( "effect org changed", change ); + self.origin = change; + } +} + +loopfxChangeDelay( ent ) +{ + self endon( "death" ); + ent waittill( "effect delay changed", change ); +} + +loopfxDeletion( ent ) +{ + self endon( "death" ); + ent waittill( "effect deleted" ); + self delete(); +} + +loopfxStop( timeout ) +{ + self endon( "death" ); + wait( timeout ); + self.looper delete(); +} + +loopSound( sound, Pos, waittime ) +{ +// level thread print_org ("loopSound", sound, Pos, waittime); + level thread loopSoundthread( sound, Pos, waittime ); +} + +loopSoundthread( sound, pos, waittime ) +{ + org = spawn( "script_origin", ( pos ) ); + + org.origin = pos; +// println ("hello1 ", org.origin, sound); + org playLoopSound( sound ); +} + +gunfireloopfx( fxId, fxPos, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ) +{ + thread gunfireloopfxthread( fxId, fxPos, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ); +} + +gunfireloopfxthread( fxId, fxPos, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ) +{ + level endon( "stop all gunfireloopfx" ); + waitframe(); + + if ( betweenSetsMax < betweenSetsMin ) + { + temp = betweenSetsMax; + betweenSetsMax = betweenSetsMin; + betweenSetsMin = temp; + } + + betweenSetsBase = betweenSetsMin; + betweenSetsRange = betweenSetsMax - betweenSetsMin; + + if ( shotdelayMax < shotdelayMin ) + { + temp = shotdelayMax; + shotdelayMax = shotdelayMin; + shotdelayMin = temp; + } + + shotdelayBase = shotdelayMin; + shotdelayRange = shotdelayMax - shotdelayMin; + + if ( shotsMax < shotsMin ) + { + temp = shotsMax; + shotsMax = shotsMin; + shotsMin = temp; + } + + shotsBase = shotsMin; + shotsRange = shotsMax - shotsMin; + + fxEnt = spawnFx( level._effect[ fxId ], fxPos ); + + if ( !level.createFX_enabled ) + fxEnt willNeverChange(); + + for ( ;; ) + { + shotnum = shotsBase + randomint( shotsRange ); + for ( i = 0;i < shotnum;i++ ) + { + triggerFx( fxEnt ); + + wait( shotdelayBase + randomfloat( shotdelayRange ) ); + } + wait( betweenSetsBase + randomfloat( betweenSetsRange ) ); + } +} + +gunfireloopfxVec( fxId, fxPos, fxPos2, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ) +{ + thread gunfireloopfxVecthread( fxId, fxPos, fxPos2, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ); +} + +gunfireloopfxVecthread( fxId, fxPos, fxPos2, shotsMin, shotsMax, shotdelayMin, shotdelayMax, betweenSetsMin, betweenSetsMax ) +{ + level endon( "stop all gunfireloopfx" ); + waitframe(); + + if ( betweenSetsMax < betweenSetsMin ) + { + temp = betweenSetsMax; + betweenSetsMax = betweenSetsMin; + betweenSetsMin = temp; + } + + betweenSetsBase = betweenSetsMin; + betweenSetsRange = betweenSetsMax - betweenSetsMin; + + if ( shotdelayMax < shotdelayMin ) + { + temp = shotdelayMax; + shotdelayMax = shotdelayMin; + shotdelayMin = temp; + } + + shotdelayBase = shotdelayMin; + shotdelayRange = shotdelayMax - shotdelayMin; + + if ( shotsMax < shotsMin ) + { + temp = shotsMax; + shotsMax = shotsMin; + shotsMin = temp; + } + + shotsBase = shotsMin; + shotsRange = shotsMax - shotsMin; + + fxPos2 = vectornormalize( fxPos2 - fxPos ); + + fxEnt = spawnFx( level._effect[ fxId ], fxPos, fxPos2 ); + + if ( !level.createFX_enabled ) + fxEnt willNeverChange(); + + for ( ;; ) + { + shotnum = shotsBase + randomint( shotsRange ); + for ( i = 0;i < int( shotnum / level.fxfireloopmod );i++ ) + { + triggerFx( fxEnt ); + delay = ( ( shotdelayBase + randomfloat( shotdelayRange ) ) * level.fxfireloopmod ); + if ( delay < .05 ) + delay = .05; + wait delay; + } + wait( shotdelayBase + randomfloat( shotdelayRange ) ); + wait( betweenSetsBase + randomfloat( betweenSetsRange ) ); + } +} + +setfireloopmod( value ) +{ + level.fxfireloopmod = 1 / value; +} + +setup_fx() +{ + if ( ( !isdefined( self.script_fxid ) ) || ( !isdefined( self.script_fxcommand ) ) || ( !isdefined( self.script_delay ) ) ) + { +// println (self.script_fxid); +// println (self.script_fxcommand); +// println (self.script_delay); +// println ("Effect at origin ", self.origin," doesn't have script_fxid/script_fxcommand/script_delay"); +// self delete(); + return; + } + +// println ("^a Command:", self.script_fxcommand, " Effect:", self.script_fxID, " Delay:", self.script_delay, " ", self.origin); + if ( isdefined( self.model ) ) + if ( self.model == "toilet" ) + { + self thread burnville_paratrooper_hack(); + return; + } + + org = undefined; + if ( isdefined( self.target ) ) + { + ent = getent( self.target, "targetname" ); + if ( isdefined( ent ) ) + org = ent.origin; + } + + fxStart = undefined; + if ( isdefined( self.script_fxstart ) ) + fxStart = self.script_fxstart; + + fxStop = undefined; + if ( isdefined( self.script_fxstop ) ) + fxStop = self.script_fxstop; + + if ( self.script_fxcommand == "OneShotfx" ) + OneShotfx( self.script_fxId, self.origin, self.script_delay, org ); + if ( self.script_fxcommand == "loopfx" ) + loopfx( self.script_fxId, self.origin, self.script_delay, org, fxStart, fxStop ); + if ( self.script_fxcommand == "loopsound" ) + loopsound( self.script_fxId, self.origin, self.script_delay ); + + self delete(); +} + +burnville_paratrooper_hack() +{ + normal = ( 0, 0, self.angles[ 1 ] ); +// println ("z: paratrooper fx hack: ", normal); + id = level._effect[ self.script_fxId ]; + origin = self.origin; + +// if (isdefined (self.script_delay)) +// wait (self.script_delay); + + wait 1; + level thread burnville_paratrooper_hack_loop( normal, origin, id ); + self delete(); +} + +burnville_paratrooper_hack_loop( normal, origin, id ) +{ + while ( 1 ) + { + // iprintln ("z: playing paratrooper fx", origin); + + playfx( id, origin ); + wait( 30 + randomfloat( 40 ) ); + } +} + +create_triggerfx() +{ + //assert (isdefined(self.looper)); + if( ! verify_effects_assignment( self.v[ "fxid" ] ) ) + return; + + self.looper = spawnFx( level._effect[ self.v[ "fxid" ] ], self.v[ "origin" ], self.v[ "forward" ], self.v[ "up" ] ); + triggerFx( self.looper, self.v[ "delay" ] ); + + if ( !level.createFX_enabled ) + self.looper willNeverChange(); + + create_loopsound(); +} + +verify_effects_assignment( effectID ) +{ + if( isdefined ( level._effect[ effectID ] ) ) + return true; + if( ! isdefined( level._missing_FX ) ) + level._missing_FX = []; + level._missing_FX[ self.v[ "fxid" ] ] = effectID; + verify_effects_assignment_print( effectID ); + return false; +} + +verify_effects_assignment_print( effectID ) +{ + + level notify ( "verify_effects_assignment_print" ); + level endon ( "verify_effects_assignment_print" ); + wait .05; //allow errors on the same frame to que up before printing + + println("Error:"); + println("Error:**********MISSING EFFECTS IDS**********"); + keys = getarraykeys( level._missing_FX ); + foreach( key in keys ) + { + println( "Error: Missing Effects ID assignment for: "+ key ); + } + println("Error:"); + + assertmsg( "Missing Effects ID assignments ( see console )" ); +} + +OneShotfxthread() +{ + wait(0.05); + + if ( !PlatformMatches() ) + { + return; + } + + if ( self.v[ "delay" ] > 0 ) + wait self.v[ "delay" ]; + + [[ level.func[ "create_triggerfx" ] ]](); +} + +//--------------------------------------------------------- +// Reactive Section +//--------------------------------------------------------- +CONST_MAX_REACTIVE_SOUND_ENTS = 4; +CONST_NEXT_PLAY_TIME = 3000; +add_reactive_fx() +{ + if ( !PlatformMatches() ) + { + return; + } + + // MP should not start this thread in release + if ( !isSP() && GetDVar( "createfx" ) == "" ) + { + return; + } + + if ( !IsDefined( level._fx.reactive_thread ) ) + { + level._fx.reactive_thread = true; + level thread reactive_fx_thread(); + } + + if ( !IsDefined( level._fx.reactive_fx_ents ) ) + { + level._fx.reactive_fx_ents = []; + } + + level._fx.reactive_fx_ents[ level._fx.reactive_fx_ents.size ] = self; + self.next_reactive_time = 3000; +} + +reactive_fx_thread() +{ + if ( !isSp() ) + { + if ( GetDvar( "createfx" ) == "on" ) + { + // wait till the player spawns and createfxlogic starts before continuing. + flag_wait( "createfx_started" ); + } + } + + // TODO: Switch to using level notify instead + // HOWEVER!!! we will need the trigger_radius for when we are in CREATEFX mode for MP only!!! + // use level.mp_createfx condition +// trigger = Spawn( "trigger_radius", level.player.origin, 0, 5000, 5000 ); +// trigger SetCanDamage( true ); + +// trigger EnableLinkTo(); +// trigger LinkTo( level.player ); + + level._fx.reactive_sound_ents = []; + + explosion_radius = 256; + + while ( 1 ) + { +// trigger waittill( "damage", dmg, attacker, dir_vec, point, type ); + level waittill( "code_damageradius", attacker, explosion_radius, point, weapon_name ); + + ents = sort_reactive_ents( point, explosion_radius ); + + foreach ( i, ent in ents ) + ent thread play_reactive_fx( i ); + } + } + +vector2d( vec ) +{ + return ( vec[ 0 ], vec[ 1 ], 0 ); +} + +sort_reactive_ents( point, explosion_radius ) +{ + closest = []; + time = GetTime(); + foreach ( ent in level._fx.reactive_fx_ents ) + { + if ( ent.next_reactive_time > time ) + continue; + + radius_squared = ent.v[ "reactive_radius" ] + explosion_radius; + radius_squared *= radius_squared; + if ( DistanceSquared( point, ent.v[ "origin" ] ) < radius_squared ) + { + closest[ closest.size ] = ent; + } + } + + foreach ( ent in closest ) + { + playerToEnt = vector2d( ent.v[ "origin" ] - level.player.origin ); + playerToPoint = vector2d( point - level.player.origin ); + vec1 = VectorNormalize( playerToEnt ); + vec2 = VectorNormalize( playerToPoint ); + ent.dot = VectorDot( vec1, vec2 ); + } + + // Sort from lowest dot to greatest + for ( i = 0; i < closest.size - 1; i++ ) + { + for ( j = i + 1; j < closest.size; j++ ) + { + if ( closest[ i ].dot > closest[ j ].dot ) + { + temp = closest[ i ]; + closest[ i ] = closest[ j ]; + closest[ j ] = temp; + } + } + } + + // remove the .origin and .dot since we're done with sortbydistance() + foreach ( ent in closest ) + { + ent.origin = undefined; + ent.dot = undefined; + } + + // Make sure we only have 4 + for ( i = CONST_MAX_REACTIVE_SOUND_ENTS; i < closest.size; i++ ) + { + closest[ i ] = undefined; + } + + return closest; +} + +play_reactive_fx( num ) +{ + sound_ent = get_reactive_sound_ent(); + + if ( !IsDefined( sound_ent ) ) + return; + +/# + self.is_playing = true; +#/ + + self.next_reactive_time = GeTTime() + CONST_NEXT_PLAY_TIME; + sound_ent.origin = self.v[ "origin" ]; + sound_ent.is_playing = true; + + wait( num * RandomFloatRange( 0.05, 0.1 ) ); + + if ( isSP() ) + { + sound_ent PlaySound( self.v[ "soundalias" ], "sounddone" ); + sound_ent waittill( "sounddone" ); + } + else + { + sound_ent PlaySound( self.v[ "soundalias" ] ); + wait( 2 ); + } + + // wait for sounddone to be removed compeletely + // bug in prague port to iw6 where in the same frame this got called again after the sounddone notify + // odd thing, even 0.05 doesn't work. Code should fix this! + wait( 0.1 ); + sound_ent.is_playing = false; + +/# + self.is_playing = undefined; +#/ +} + +get_reactive_sound_ent() +{ + foreach ( ent in level._fx.reactive_sound_ents ) + { + if ( !ent.is_playing ) + return ent; + } + + if ( level._fx.reactive_sound_ents.size < CONST_MAX_REACTIVE_SOUND_ENTS ) + { + ent = Spawn( "script_origin", ( 0, 0, 0 ) ); + ent.is_playing = false; + + level._fx.reactive_sound_ents[ level._fx.reactive_sound_ents.size ] = ent; + + return ent; + } + + return undefined; +} \ No newline at end of file diff --git a/raw/common_scripts/utility.gsc b/raw/common_scripts/utility.gsc new file mode 100644 index 0000000..9c36828 --- /dev/null +++ b/raw/common_scripts/utility.gsc @@ -0,0 +1,5660 @@ + +/* +============= +///ScriptDocBegin +"Name: noself_func( , , , , )" +"Summary: Runs a function from level.func, if it exists. Stand alone, doesn't run on anything. Useful for common scripts where a code function may not exist in one codebase or the other." +"Module: Utility" +"CallOn: An entity" +"MandatoryArg: : String reference to level.func array." +"OptionalArg: : " +"OptionalArg: : " +"OptionalArg: : " +"OptionalArg: : " +"Example: noself_func( "setsaveddvar", "r_spotlightbrightness", maxVal );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +noself_func( func, parm1, parm2, parm3, parm4 ) +{ + if ( !isdefined( level.func ) ) + return; + if ( !isdefined( level.func[ func ] ) ) + return; + + if ( !isdefined( parm1 ) ) + { + call [[ level.func[ func ] ]](); + return; + } + + if ( !isdefined( parm2 ) ) + { + call [[ level.func[ func ] ]]( parm1 ); + return; + } + if ( !isdefined( parm3 ) ) + { + call [[ level.func[ func ] ]]( parm1, parm2 ); + return; + } + if ( !isdefined( parm4 ) ) + { + call [[ level.func[ func ] ]]( parm1, parm2, parm3 ); + return; + } + + call [[ level.func[ func ] ]]( parm1, parm2, parm3, parm4 ); +} + +/* +============= +///ScriptDocBegin +"Name: self_func( , , , , )" +"Summary: Runs a function from level.func, if it exists. Runs on whatever calls it. Useful for common scripts where a code function may not exist in one codebase or the other." +"Module: Utility" +"CallOn: An entity" +"MandatoryArg: : String reference to level.func array." +"OptionalArg: : " +"OptionalArg: : " +"OptionalArg: : " +"OptionalArg: : " +"Example: level.player self_func( "some_player_function", 1, 2 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +self_func( func, parm1, parm2, parm3, parm4 ) +{ + if ( !isdefined( level.func[ func ] ) ) + return; + + if ( !isdefined( parm1 ) ) + { + self call [[ level.func[ func ] ]](); + return; + } + + if ( !isdefined( parm2 ) ) + { + self call [[ level.func[ func ] ]]( parm1 ); + return; + } + if ( !isdefined( parm3 ) ) + { + self call [[ level.func[ func ] ]]( parm1, parm2 ); + return; + } + if ( !isdefined( parm4 ) ) + { + self call [[ level.func[ func ] ]]( parm1, parm2, parm3 ); + return; + } + + self call [[ level.func[ func ] ]]( parm1, parm2, parm3, parm4 ); +} + +/* +============= +///ScriptDocBegin +"Name: randomvector( )" +"Summary: returns a random vector centered on " +"Module: Vector" +"CallOn: Level" +"MandatoryArg: : " +"Example: direction = randomvector( 1 )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +randomvector( num ) +{ + return( randomfloat( num ) - num * 0.5, randomfloat( num ) - num * 0.5, randomfloat( num ) - num * 0.5 ); +} + +/* +============= +///ScriptDocBegin +"Name: randomvectorrange( , )" +"Summary: returns a random vector centered between and " +"Module: Vector" +"CallOn: Level" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: direction = randomvectorrange( 5, 10 )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +randomvectorrange( num_min, num_max ) +{ + assert( isdefined( num_min ) ); + assert( isdefined( num_max ) ); + + x = randomfloatrange( num_min, num_max ); + if ( randomint( 2 ) == 0 ) + x *= -1; + + y = randomfloatrange( num_min, num_max ); + if ( randomint( 2 ) == 0 ) + y *= -1; + + z = randomfloatrange( num_min, num_max ); + if ( randomint( 2 ) == 0 ) + z *= -1; + + return( x, y, z ); +} + +/* +============= +///ScriptDocBegin +"Name: randomvectorincone( , )" +"Summary: returns a random vector in cone specified by direction and angle" +"Module: Vector" +"CallOn: Level" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: direction = randomvectorincone( (1,0,0), 120 )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +randomvectorincone( coneDirection, coneAngleDegrees ) +{ + phi = RandomFloat(coneAngleDegrees); + theta = RandomFloat(360); + + sinPhi = sin(phi); + cosPhi = cos(phi); + sinTheta = sin(theta); + cosTheta = cos(theta); + + v = ( cosPhi, cosTheta * sinPhi, sinTheta * sinPhi ); + + return RotateVector( v, VectorToAngles( coneDirection ) ); +} + +sign( x ) +{ + if ( x >= 0 ) + return 1; + return - 1; +} + +// Modulo: returns the part left over when dividend is divided by divisor. If divisor is <0, result will be <0. +mod( dividend, divisor ) +{ + q = Int( dividend / divisor ); + if ( dividend * divisor < 0 ) q -= 1; + return dividend - ( q * divisor ); +} + +toString( num ) +{ + return( "" + num ); +} + +track( spot_to_track ) +{ + if ( isdefined( self.current_target ) ) + { + if ( spot_to_track == self.current_target ) + return; + } + self.current_target = spot_to_track; +} + +get_enemy_team( team ) +{ + assertEx( team != "neutral", "Team must be allies or axis" ); + + teams = []; + teams[ "axis" ] = "allies"; + teams[ "allies" ] = "axis"; + + return teams[ team ]; +} + + +clear_exception( type ) +{ + assert( isdefined( self.exception[ type ] ) ); + self.exception[ type ] = anim.defaultException; +} + +set_exception( type, func ) +{ + assert( isdefined( self.exception[ type ] ) ); + self.exception[ type ] = func; +} + +set_all_exceptions( exceptionFunc ) +{ + keys = getArrayKeys( self.exception ); + for ( i = 0; i < keys.size; i++ ) + { + self.exception[ keys[ i ] ] = exceptionFunc; + } +} + +/* +============= +///ScriptDocBegin +"Name: cointoss()" +"Summary: 50/50 returns true" +"Module: Utility" +"CallOn: Level" +"Example: if(cointoss())" +"SPMP: both" +///ScriptDocEnd +============= +*/ +cointoss() +{ + return randomint( 100 ) >= 50 ; +} + + +choose_from_weighted_array( values, weights ) +{ + assert( values.size == weights.size ); + + randomval = randomint( weights[ weights.size - 1 ] + 1 ); + + for ( i = 0; i < weights.size; i++ ) + { + if ( randomval <= weights[i] ) + return values[i]; + } +} + +get_cumulative_weights( weights ) +{ + cumulative_weights = []; + + sum = 0; + for ( i = 0; i < weights.size; i++ ) + { + sum += weights[i]; + cumulative_weights[i] = sum; + } + + return cumulative_weights; +} + + +waittill_string( msg, ent ) +{ + if ( msg != "death" ) + self endon( "death" ); + + ent endon( "die" ); + self waittill( msg ); + ent notify( "returned", msg ); +} + +waittill_string_parms( msg, ent ) +{ + if ( msg != "death" ) + self endon( "death" ); + + ent endon( "die" ); + self waittill( msg, parm1, parm2, parm3, parm4, parm5, parm6, parm7, parm8, parm9, parm10 ); + + result = []; + result[0] = msg; + if ( IsDefined( parm1 ) ) + result[1] = parm1; + if ( IsDefined( parm2 ) ) + result[2] = parm2; + if ( IsDefined( parm3 ) ) + result[3] = parm3; + if ( IsDefined( parm4 ) ) + result[4] = parm4; + if ( IsDefined( parm5 ) ) + result[5] = parm5; + if ( IsDefined( parm6 ) ) + result[6] = parm6; + if ( IsDefined( parm7 ) ) + result[7] = parm7; + if ( IsDefined( parm8 ) ) + result[8] = parm8; + if ( IsDefined( parm9 ) ) + result[9] = parm9; + if ( IsDefined( parm10 ) ) + result[10] = parm10; + + ent notify( "returned", result ); +} + +waittill_string_no_endon_death( msg, ent ) +{ + ent endon( "die" ); + self waittill( msg ); + ent notify( "returned", msg ); +} + +waittill_multiple( string1, string2, string3, string4, string5 ) +{ + self endon( "death" ); + ent = SpawnStruct(); + ent.threads = 0; + + if ( isdefined( string1 ) ) + { + self childthread waittill_string( string1, ent ); + ent.threads++; + } + if ( isdefined( string2 ) ) + { + self childthread waittill_string( string2, ent ); + ent.threads++; + } + if ( isdefined( string3 ) ) + { + self childthread waittill_string( string3, ent ); + ent.threads++; + } + if ( isdefined( string4 ) ) + { + self childthread waittill_string( string4, ent ); + ent.threads++; + } + if ( isdefined( string5 ) ) + { + self childthread waittill_string( string5, ent ); + ent.threads++; + } + + while ( ent.threads ) + { + ent waittill( "returned" ); + ent.threads--; + } + + ent notify( "die" ); +} + +waittill_multiple_ents( ent1, string1, ent2, string2, ent3, string3, ent4, string4 ) +{ + self endon( "death" ); + ent = SpawnStruct(); + ent.threads = 0; + + if ( isdefined( ent1 ) ) + { + assert( isdefined( string1 ) ); + ent1 childthread waittill_string( string1, ent ); + ent.threads++; + } + if ( isdefined( ent2 ) ) + { + assert( isdefined( string2 ) ); + ent2 childthread waittill_string( string2, ent ); + ent.threads++; + } + if ( isdefined( ent3 ) ) + { + assert( isdefined( string3 ) ); + ent3 childthread waittill_string( string3, ent ); + ent.threads++; + } + if ( isdefined( ent4 ) ) + { + assert( isdefined( string4 ) ); + ent4 childthread waittill_string( string4, ent ); + ent.threads++; + } + + while ( ent.threads ) + { + ent waittill( "returned" ); + ent.threads--; + } + + ent notify( "die" ); +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_return( , , , , , )" +"Summary: Waits for any of several messages then returns what it was." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : A string to wait on" +"MandatoryArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"Example: msg = level.player waittill_any_return( "weapon_fired", "player_flash", "player_frag" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_return( string1, string2, string3, string4, string5, string6, string7 ) +{ + if ( ( !isdefined( string1 ) || string1 != "death" ) && + ( !isdefined( string2 ) || string2 != "death" ) && + ( !isdefined( string3 ) || string3 != "death" ) && + ( !isdefined( string4 ) || string4 != "death" ) && + ( !isdefined( string5 ) || string5 != "death" ) && + ( !isdefined( string6 ) || string6 != "death" ) && + ( !isdefined( string7 ) || string7 != "death" ) ) + self endon( "death" ); + + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string( string1, ent ); + + if ( isdefined( string2 ) ) + self childthread waittill_string( string2, ent ); + + if ( isdefined( string3 ) ) + self childthread waittill_string( string3, ent ); + + if ( isdefined( string4 ) ) + self childthread waittill_string( string4, ent ); + + if ( isdefined( string5 ) ) + self childthread waittill_string( string5, ent ); + + if ( isdefined( string6 ) ) + self childthread waittill_string( string6, ent ); + + if ( isdefined( string7 ) ) + self childthread waittill_string( string7, ent ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_return_parms( , , , , , , )" +"Summary: Waits for any of several messages then returns what it was with all the parameters if any as an array." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : A string to wait on" +"MandatoryArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"Example: results = level.player waittill_any_return_parms( "weapon_fired", "player_flash", "player_frag" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_return_parms( string1, string2, string3, string4, string5, string6, string7, string8 ) +{ + if ( ( !isdefined( string1 ) || string1 != "death" ) && + ( !isdefined( string2 ) || string2 != "death" ) && + ( !isdefined( string3 ) || string3 != "death" ) && + ( !isdefined( string4 ) || string4 != "death" ) && + ( !isdefined( string5 ) || string5 != "death" ) && + ( !isdefined( string6 ) || string6 != "death" ) && + ( !isdefined( string7 ) || string7 != "death" ) && + ( !isdefined( string8 ) || string8 != "death" ) ) + self endon( "death" ); + + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string_parms( string1, ent ); + + if ( isdefined( string2 ) ) + self childthread waittill_string_parms( string2, ent ); + + if ( isdefined( string3 ) ) + self childthread waittill_string_parms( string3, ent ); + + if ( isdefined( string4 ) ) + self childthread waittill_string_parms( string4, ent ); + + if ( isdefined( string5 ) ) + self childthread waittill_string_parms( string5, ent ); + + if ( isdefined( string6 ) ) + self childthread waittill_string_parms( string6, ent ); + + if ( isdefined( string7 ) ) + self childthread waittill_string_parms( string7, ent ); + + if ( isdefined( string8 ) ) + self childthread waittill_string_parms( string8, ent ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_return_no_endon_death( , , , , , )" +"Summary: Waits for any of several messages then returns what it was. Doesn't endon death." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : A string to wait on" +"MandatoryArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"Example: msg = level.player waittill_any_return_no_endon_death( "weapon_fired", "player_flash", "player_frag" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_return_no_endon_death( string1, string2, string3, string4, string5, string6 ) +{ + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string_no_endon_death( string1, ent ); + + if ( isdefined( string2 ) ) + self childthread waittill_string_no_endon_death( string2, ent ); + + if ( isdefined( string3 ) ) + self childthread waittill_string_no_endon_death( string3, ent ); + + if ( isdefined( string4 ) ) + self childthread waittill_string_no_endon_death( string4, ent ); + + if ( isdefined( string5 ) ) + self childthread waittill_string_no_endon_death( string5, ent ); + + if ( isdefined( string6 ) ) + self childthread waittill_string_no_endon_death( string6, ent ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_in_array_return( )" +"Summary: Waits for any of several messages then returns what it was." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : An array of strings to wait on" +"Example: msg = level.player waittill_any_in_array_return( array );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_in_array_return( string_array ) +{ + ent = SpawnStruct(); + hasDeath = false; + foreach( string in string_array ) + { + self childthread waittill_string( string, ent ); + + if( string == "death" ) + hasDeath = true; + } + if( !hasDeath ) + self endon( "death" ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_in_array_return_no_endon_death( )" +"Summary: Waits for any of several messages then returns what it was. Doesn't endon death." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : An array of strings to wait on" +"Example: msg = level.player waittill_any_in_array_return_no_endon_death( array );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_in_array_return_no_endon_death( string_array ) +{ + ent = SpawnStruct(); + foreach( string in string_array ) + { + self childthread waittill_string_no_endon_death( string, ent ); + } + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_in_array_or_timeout( , )" +"Summary: Waits for any of several messages then returns what it was, or returns "timeout" if the time limit is reached." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : An array of strings to wait on" +"Example: msg = level.player waittill_any_in_array_or_timeout( array, 3.0 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_in_array_or_timeout( string_array, timeOut ) +{ + ent = SpawnStruct(); + hasDeath = false; + foreach( string in string_array ) + { + self childthread waittill_string( string, ent ); + + if( string == "death" ) + hasDeath = true; + } + if( !hasDeath ) + self endon( "death" ); + + ent childthread _timeout( timeOut ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_in_array_or_timeout_no_endon_death( , )" +"Summary: Waits for any of several messages then returns what it was, or returns "timeout" if the time limit is reached." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : An array of strings to wait on" +"Example: msg = level.player waittill_any_in_array_or_timeout_no_endon_death( array, 3.0 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_in_array_or_timeout_no_endon_death( string_array, timeOut ) +{ + ent = SpawnStruct(); + foreach( string in string_array ) + { + self childthread waittill_string_no_endon_death( string, ent ); + } + + ent thread _timeout( timeOut ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_timeout( , , , , , , )" +"Summary: Waits for any of several messages then returns what it was, or returns "timeout" if the time limit is reached" +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : Time (in seconds) to stop waiting and return "timeout" +"MandatoryArg: : A string to wait on" +"MandatoryArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"OptionalArg: : A string to wait on" +"Example: msg = level.player waittill_any_timeout( 10, "weapon_fired", "player_flash", "player_frag" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_timeout( timeOut, string1, string2, string3, string4, string5, string6 ) +{ + if ( ( !isdefined( string1 ) || string1 != "death" ) && + ( !isdefined( string2 ) || string2 != "death" ) && + ( !isdefined( string3 ) || string3 != "death" ) && + ( !isdefined( string4 ) || string4 != "death" ) && + ( !isdefined( string5 ) || string5 != "death" ) && + ( !isdefined( string6 ) || string6 != "death" ) ) + self endon( "death" ); + + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string( string1, ent ); + + if ( isdefined( string2 ) ) + self childthread waittill_string( string2, ent ); + + if ( isdefined( string3 ) ) + self childthread waittill_string( string3, ent ); + + if ( isdefined( string4 ) ) + self childthread waittill_string( string4, ent ); + + if ( isdefined( string5 ) ) + self childthread waittill_string( string5, ent ); + + if ( isdefined( string6 ) ) + self childthread waittill_string( string6, ent ); + + ent childthread _timeout( timeOut ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + + +_timeout( delay ) +{ + self endon( "die" ); + + wait( delay ); + self notify( "returned", "timeout" ); +} + +/* +============= +///ScriptDocBegin +"Name: waittill_any_timeout_no_endon_death( , , , , , )" +"Summary: The waittill_any_timeout ends on death. This one will not." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any_timeout_no_endon_death( timeOut, string1, string2, string3, string4, string5 ) +{ + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string_no_endon_death( string1, ent ); + + if ( isdefined( string2 ) ) + self childthread waittill_string_no_endon_death( string2, ent ); + + if ( isdefined( string3 ) ) + self childthread waittill_string_no_endon_death( string3, ent ); + + if ( isdefined( string4 ) ) + self childthread waittill_string_no_endon_death( string4, ent ); + + if ( isdefined( string5 ) ) + self childthread waittill_string_no_endon_death( string5, ent ); + + ent childthread _timeout( timeOut ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + + +/* +============= +///ScriptDocBegin +"Name: waittill_any( , , , , , , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : a notify on which the entity should wait" +"OptionalArg: - : optional other notifies to wait for" +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_any( string1, string2, string3, string4, string5, string6, string7, string8 ) +{ + assert( isdefined( string1 ) ); + + if ( isdefined( string2 ) ) + self endon( string2 ); + + if ( isdefined( string3 ) ) + self endon( string3 ); + + if ( isdefined( string4 ) ) + self endon( string4 ); + + if ( isdefined( string5 ) ) + self endon( string5 ); + + if ( isdefined( string6 ) ) + self endon( string6 ); + + if ( isdefined( string7 ) ) + self endon( string7 ); + + if ( isdefined( string8 ) ) + self endon( string8 ); + + self waittill( string1 ); +} + +waittill_any_ents( ent1, string1, ent2, string2, ent3, string3, ent4, string4, ent5, string5, ent6, string6, ent7, string7 ) +{ + assert( isdefined( ent1 ) ); + assert( isdefined( string1 ) ); + + if ( ( isdefined( ent2 ) ) && ( isdefined( string2 ) ) ) + ent2 endon( string2 ); + + if ( ( isdefined( ent3 ) ) && ( isdefined( string3 ) ) ) + ent3 endon( string3 ); + + if ( ( isdefined( ent4 ) ) && ( isdefined( string4 ) ) ) + ent4 endon( string4 ); + + if ( ( isdefined( ent5 ) ) && ( isdefined( string5 ) ) ) + ent5 endon( string5 ); + + if ( ( isdefined( ent6 ) ) && ( isdefined( string6 ) ) ) + ent6 endon( string6 ); + + if ( ( isdefined( ent7 ) ) && ( isdefined( string7 ) ) ) + ent7 endon( string7 ); + + ent1 waittill( string1 ); +} + +/* +============= +///ScriptDocBegin +"Name: isFlashed()" +"Summary: Returns true if the player or an AI is flashed" +"Module: Utility" +"CallOn: An AI" +"Example: flashed = level.price isflashed();" +"SPMP: both" +///ScriptDocEnd +============= +*/ +isFlashed() +{ + time = gettime(); + + if( IsDefined(self.flashEndTime) && (time < self.flashEndTime) ) + return true; + + if( IsDefined(self.concussionEndTime) && (time < self.concussionEndTime) ) + return true; + + return false; +} + + /* + ============= +///ScriptDocBegin +"Name: flag_exist( )" +"Summary: checks to see if a flag exists" +"Module: Flag" +"MandatoryArg: : name of the flag to check" +"Example: if( flag_exist( "hq_cleared" ) );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_exist( message ) +{ + return isdefined( level.flag[ message ] ); +} + + /* + ============= +///ScriptDocBegin +"Name: flag( )" +"Summary: Checks if the flag is set. Returns true or false." +"Module: Flag" +"MandatoryArg: : name of the flag to check" +"Example: if ( flag( "hq_cleared" ) )" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +flag( message ) +{ + assertEx( isdefined( message ), "Tried to check flag but the flag was not defined." ); + assertEx( isdefined( level.flag[ message ] ), "Tried to check flag " + message + " but the flag was not initialized." ); + + return level.flag[ message ]; +} + + +init_flags() +{ + level.flag = []; + level.flags_lock = []; + level.generic_index = 0; + + if ( !isdefined( level.sp_stat_tracking_func ) ) + level.sp_stat_tracking_func = ::empty_init_func; + + level.flag_struct = SpawnStruct(); + level.flag_struct assign_unique_id(); +} + + /* + ============= +///ScriptDocBegin +"Name: flag_init( )" +"Summary: Initialize a flag to be used. All flags must be initialized before using flag_set or flag_wait" +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of the flag to create" +"Example: flag_init( "hq_cleared" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_init( message ) +{ + if ( !isDefined( level.flag ) ) + { + init_flags(); + } + + /# + if ( isdefined( level.first_frame ) && level.first_frame == -1 ) + assertEx( !isDefined( level.flag[ message ] ), "Attempt to reinitialize existing message: " + message ); + #/ + + level.flag[ message ] = false; +/# + // lock check +#/ + if ( !isdefined( level.trigger_flags ) ) + { + init_trigger_flags(); + level.trigger_flags[ message ] = []; + } + else + if ( !isdefined( level.trigger_flags[ message ] ) ) + { + level.trigger_flags[ message ] = []; + } + + if ( issuffix( message, "aa_" ) ) + { + thread [[ level.sp_stat_tracking_func ]]( message ); + } +} + +empty_init_func( empty ) +{ +} + +issuffix( msg, suffix ) +{ + if ( suffix.size > msg.size ) + return false; + + for ( i = 0; i < suffix.size; i++ ) + { + if ( msg[ i ] != suffix[ i ] ) + return false; + } + return true; +} + /* + ============= +///ScriptDocBegin +"Name: flag_set( , )" +"Summary: Sets the specified flag, all scripts using flag_wait will now continue." +"Module: Flag" +"MandatoryArg: : name of the flag to set" +"OptionalArg: : Pass an entity with the flag_set" +"Example: flag_set( "hq_broiled" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_set( message, setter ) +{ +/# + assertEx( isDefined( level.flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message ); + //lock check +#/ + + level.flag[ message ] = true; + set_trigger_flag_permissions( message ); + if ( isdefined( setter ) ) + { + level notify( message, setter );// notify needs to be very last thing called + } + else + { + level notify( message );// notify needs to be very last thing called + } +} + +assign_unique_id() +{ + self.unique_id = "generic" + level.generic_index; + level.generic_index++; +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait( )" +"Summary: Waits until the specified flag is set." +"Module: Flag" +"MandatoryArg: : name of the flag to wait on" +"Example: flag_wait( "hq_cleared" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait( msg ) +{ + other = undefined; + while ( !flag( msg ) ) + { + other = undefined; + level waittill( msg, other ); + } + if ( isdefined( other ) ) + return other; +} + + /* + ============= +///ScriptDocBegin +"Name: flag_clear( , )" +"Summary: Clears the specified flag." +"Module: Flag" +"MandatoryArg: : name of the flag to clear" +"Example: flag_clear( "hq_cleared" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_clear( message ) +{ +/# + assertEx( isDefined( level.flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message ); + // lock implementation tbd +#/ + //do this check so we don't unneccessarily send a notify + if ( !flag( message ) ) + return; + + level.flag[ message ] = false; + + set_trigger_flag_permissions( message ); + level notify( message );// the notify needs to be the very last thing called in this function +} + +/* +============= +///ScriptDocBegin +"Name: flag_waitopen( )" +"Summary: Waits for the flag to open" +"Module: Flag" +"MandatoryArg: : The flag" +"Example: flag_waitopen( "get_me_bagels" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +flag_waitopen( msg ) +{ + while ( flag( msg ) ) + level waittill( msg ); +} + +/* +============= +///ScriptDocBegin +"Name: waittill_either( , )" +"Summary: Waits until either message, on self" +"Module: Utility" +"CallOn: An entity or the level" +"MandatoryArg: : First msg to wait on" +"MandatoryArg: : Second msg to wait on" +"Example: level waittill_either( "yo", "no" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_either( msg1, msg2 ) +{ + self endon( msg1 ); + self waittill( msg2 ); +} + +/* + ============= +///ScriptDocBegin +"Name: array_thread( , , , , )" +"Summary: Threads the < process > function on every entity in the < entities > array. The entity will become "self" in the specified function." +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to thread the process" +"MandatoryArg: : pointer to a script function" +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"Example: array_thread( array_of_guys, ::set_ignoreme, false );" +"SPMP: both" +///ScriptDocEnd + ============= +*/ +array_thread( entities, process, var1, var2, var3, var4, var5, var6, var7, var8, var9 ) +{ + if ( !isdefined( var1 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]](); + return; + } + + if ( !isdefined( var2 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1 ); + return; + } + + if ( !isdefined( var3 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2 ); + return; + } + + if ( !isdefined( var4 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3 ); + return; + } + + if ( !isdefined( var5 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4 ); + return; + } + + if ( !isdefined( var6 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4, var5 ); + return; + } + + if ( !isdefined( var7 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4, var5, var6 ); + return; + } + + if ( !isdefined( var8 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4, var5, var6, var7 ); + return; + } + + if ( !isdefined( var9 ) ) + { + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4, var5, var6, var7, var8 ); + return; + } + + foreach ( ent in entities ) + ent thread [[ process ]]( var1, var2, var3, var4, var5, var6, var7, var8, var9 ); + return; +} + +/* + ============= +///ScriptDocBegin +"Name: array_call( , , , , )" +"Summary: Runs the code < process > function on every entity in the < entities > array. The entity will become "self" in the specified function." +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to thread the process" +"MandatoryArg: : pointer to a code function" +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"Example: array_call( array_of_guys, ::set_ignoreme, false );" +"SPMP: both" +///ScriptDocEnd + ============= +*/ +array_call( entities, process, var1, var2, var3 ) +{ + if ( isdefined( var3 ) ) + { + foreach ( ent in entities ) + ent call [[ process ]]( var1, var2, var3 ); + + return; + } + + if ( isdefined( var2 ) ) + { + foreach ( ent in entities ) + ent call [[ process ]]( var1, var2 ); + + return; + } + + if ( isdefined( var1 ) ) + { + foreach ( ent in entities ) + ent call [[ process ]]( var1 ); + + return; + } + + foreach ( ent in entities ) + ent call [[ process ]](); +} +/* + ============= +///ScriptDocBegin +"Name: noself_array_call( , , , , )" +"Summary: Runs the code < process > function with every entity in the < entities > array as the first parm. " +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to thread the process" +"MandatoryArg: : pointer to a code function" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"OptionalArg: : parameter 4 to pass to the process" +"Example: noself_array_call( level.introScreen.Names, ::PreCacheString );" +"SPMP: both" +///ScriptDocEnd + ============= +*/ +noself_array_call(entities, process, var2, var3, var4 ) +{ + if ( isdefined( var4 ) ) + { + foreach ( ent in entities ) + call [[ process ]]( ent, var2, var3, var4 ); + + return; + } + + if ( isdefined( var3 ) ) + { + foreach ( ent in entities ) + call [[ process ]]( ent, var2, var3 ); + + return; + } + + if ( isdefined( var2 ) ) + { + foreach ( ent in entities ) + call [[ process ]]( ent, var2 ); + + return; + } + + foreach ( ent in entities ) + call [[ process ]]( ent ); +} + +/* +============= +///ScriptDocBegin +"Name: array_thread4( , , , , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +array_thread4( entities, process, var1, var2, var3, var4 ) +{ + array_thread( entities, process, var1, var2, var3, var4 ); +} + +/* +============= +///ScriptDocBegin +"Name: array_thread5( , , , , , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +array_thread5( entities, process, var1, var2, var3, var4, var5 ) +{ + array_thread( entities, process, var1, var2, var3, var4, var5 ); +} + +/* + ============= +///ScriptDocBegin +"Name: trigger_on( , )" +"Summary: Turns a trigger on. This only needs to be called if it was previously turned off" +"Module: Trigger" +"CallOn: A trigger" +"OptionalArg: : the name corrisponding to a targetname or script_noteworthy to grab the trigger internally" +"OptionalArg: : the type( targetname, or script_noteworthy ) corrisponding to a name to grab the trigger internally" +"Example: trigger trigger_on(); -or- trigger_on( "base_trigger", "targetname" )" +"SPMP: both" +///ScriptDocEnd + ============= + */ +trigger_on( name, type ) +{ + if ( isdefined( name ) && isdefined( type ) ) + { + ents = getentarray( name, type ); + array_thread( ents, ::trigger_on_proc ); + } + else + self trigger_on_proc(); +} + +trigger_on_proc() +{ + if ( isDefined( self.realOrigin ) ) + self.origin = self.realOrigin; + self.trigger_off = undefined; +} + + + /* + ============= +///ScriptDocBegin +"Name: trigger_off( , )" +"Summary: Turns a trigger off so it can no longer be triggered." +"Module: Trigger" +"CallOn: A trigger" +"OptionalArg: : the name corrisponding to a targetname or script_noteworthy to grab the trigger internally" +"OptionalArg: : the type( targetname, or script_noteworthy ) corrisponding to a name to grab the trigger internally" +"Example: trigger trigger_off();" +"SPMP: both" +///ScriptDocEnd + ============= + */ +trigger_off( name, type ) +{ + if ( isdefined( name ) && isdefined( type ) ) + { + ents = getentarray( name, type ); + array_thread( ents, ::trigger_off_proc ); + } + else + self trigger_off_proc(); +} + +trigger_off_proc() +{ + if ( !isDefined( self.realOrigin ) ) + self.realOrigin = self.origin; + + if ( self.origin == self.realorigin ) + self.origin += ( 0, 0, -10000 ); + self.trigger_off = true; +} + +set_trigger_flag_permissions( msg ) +{ + // turns triggers on or off depending on if they have the proper flags set, based on their shift-g menu settings + + // this can be init before _load has run, thanks to AI. + if ( !isdefined( level.trigger_flags ) ) + return; + + // cheaper to do the upkeep at this time rather than with endons and waittills on the individual triggers + level.trigger_flags[ msg ] = array_removeUndefined( level.trigger_flags[ msg ] ); + array_thread( level.trigger_flags[ msg ], ::update_trigger_based_on_flags ); +} + +update_trigger_based_on_flags() +{ + true_on = true; + if ( isdefined( self.script_flag_true ) ) + { + true_on = false; + tokens = create_flags_and_return_tokens( self.script_flag_true ); + + // stay off unless all the flags are false + foreach ( token in tokens ) + { + if ( flag( token ) ) + { + true_on = true; + break; + } + } + } + + false_on = true; + if ( isdefined( self.script_flag_false ) ) + { + tokens = create_flags_and_return_tokens( self.script_flag_false ); + + // stay off unless all the flags are false + foreach ( token in tokens ) + { + if ( flag( token ) ) + { + false_on = false; + break; + } + } + } + + [[ level.trigger_func[ true_on && false_on ] ]](); +} + +create_flags_and_return_tokens( flags ) +{ + tokens = strtok( flags, " " ); + + // create the flag if level script does not + for ( i = 0; i < tokens.size; i++ ) + { + if ( !isdefined( level.flag[ tokens[ i ] ] ) ) + { + flag_init( tokens[ i ] ); + } + } + + return tokens; +} + +init_trigger_flags() +{ + level.trigger_flags = []; + level.trigger_func[ true ] = ::trigger_on; + level.trigger_func[ false ] = ::trigger_off; +} + +/* +============= +///ScriptDocBegin +"Name: getstruct( , )" +"Summary: get a struct by target, targetname,script_noteworthy, or script_linkname types, must be called after maps\_load::main();" +"Module: Struct" +"CallOn: Level" +"MandatoryArg: : name of key" +"MandatoryArg: : key type" +"Example: position = getstruct("waypoint1","targetname"); +"SPMP: both" +///ScriptDocEnd +============= +*/ + +getstruct( name, type ) +{ + assertex( isdefined( name ) && isdefined( type ), "Did not fill in name and type" ); + assertEx( isdefined( level.struct_class_names ), "Tried to getstruct before the structs were init" ); + + array = level.struct_class_names[ type ][ name ]; + if ( !isdefined( array ) ) + { + return undefined; + } + + if ( array.size > 1 ) + { + assertMsg( "getstruct used for more than one struct of type " + type + " called " + name + "." ); + return undefined; + } + return array[ 0 ]; +} + + /* + ============= +///ScriptDocBegin +"Name: getstructarray( , : " +"MandatoryArg: : " +"Example: fxemitters = getstructarray( "streetlights", "targetname" )" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +getstructarray( name, type ) +{ + assertEx( isdefined( level.struct_class_names ), "Tried to getstruct before the structs were init" ); + + array = level.struct_class_names[ type ][ name ]; + if ( !isdefined( array ) ) + return []; + return array; +} + +struct_class_init() +{ + assertEx( !isdefined( level.struct_class_names ), "level.struct_class_names is being initialized in the wrong place! It shouldn't be initialized yet." ); + + level.struct_class_names = []; + level.struct_class_names[ "target" ] = []; + level.struct_class_names[ "targetname" ] = []; + level.struct_class_names[ "script_noteworthy" ] = []; + level.struct_class_names[ "script_linkname" ] = []; + + foreach ( struct in level.struct ) + { + add_struct_to_global_array(struct); + } +} + +add_struct_to_global_array(struct) +{ + if ( isdefined( struct.targetname ) ) + { + if ( !isdefined( level.struct_class_names[ "targetname" ][ struct.targetname ] ) ) + level.struct_class_names[ "targetname" ][ struct.targetname ] = []; + + size = level.struct_class_names[ "targetname" ][ struct.targetname ].size; + level.struct_class_names[ "targetname" ][ struct.targetname ][ size ] = struct; + } + if ( isdefined( struct.target ) ) + { + if ( !isdefined( level.struct_class_names[ "target" ][ struct.target ] ) ) + level.struct_class_names[ "target" ][ struct.target ] = []; + + size = level.struct_class_names[ "target" ][ struct.target ].size; + level.struct_class_names[ "target" ][ struct.target ][ size ] = struct; + } + if ( isdefined( struct.script_noteworthy ) ) + { + if ( !isdefined( level.struct_class_names[ "script_noteworthy" ][ struct.script_noteworthy ] ) ) + level.struct_class_names[ "script_noteworthy" ][ struct.script_noteworthy ] = []; + + size = level.struct_class_names[ "script_noteworthy" ][ struct.script_noteworthy ].size; + level.struct_class_names[ "script_noteworthy" ][ struct.script_noteworthy ][ size ] = struct; + } + if ( isdefined( struct.script_linkname ) ) + { + if ( !isdefined( level.struct_class_names[ "script_linkname" ][ struct.script_linkname ] ) ) + level.struct_class_names[ "script_linkname" ][ struct.script_linkname ] = []; + + size = level.struct_class_names[ "script_linkname" ][ struct.script_linkname ].size; + level.struct_class_names[ "script_linkname" ][ struct.script_linkname ][ 0 ] = struct; + } +} + +fileprint_start( file ) +{ + /# + filename = file; + level.fileprint = 1; + level.fileprintlinecount = 0; + level.fileprint_filename = filename; + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: fileprint_map_start( )" +"Summary: starts map export with the file trees\cod3\cod3\map_source\xenon_export\ < filename > .map adds header / worldspawn entity to the map. Use this if you want to start a .map export." +"Module: Fileprint" +"CallOn: Level" +"MandatoryArg: : " +"OptionalArg: : " +"Example: fileprint_map_start( filename );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +fileprint_map_start() +{ + /# + // for the entity count + level.fileprint_mapentcount = 0; + fileprint_map_header( true ); + #/ + +} + +fileprint_map_header( bInclude_blank_worldspawn ) +{ + if ( !isdefined( bInclude_blank_worldspawn ) ) + bInclude_blank_worldspawn = false; + + /# + fileprint_launcher( "iwmap 6" ); + fileprint_launcher( "\"000_Global\" flags active" ); + fileprint_launcher( "\"The Map\" flags" ); + + if ( !bInclude_blank_worldspawn ) + return; + + fileprint_map_entity_start(); + fileprint_map_keypairprint( "classname", "worldspawn" ); + fileprint_map_entity_end(); + + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: fileprint_map_keypairprint( , )" +"Summary: prints a pair of keys to the current open map( by fileprint_map_start() )" +"Module: Fileprint" +"CallOn: Level" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: fileprint_map_keypairprint( "classname", "script_model" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +fileprint_map_keypairprint( key1, key2 ) +{ + /# + fileprint_launcher( "\"" + key1 + "\" \"" + key2 + "\"" ); + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: fileprint_map_entity_start()" +"Summary: prints entity number and opening bracket to currently opened file" +"Module: Fileprint" +"CallOn: Level" +"Example: fileprint_map_entity_start();" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +fileprint_map_entity_start() +{ + /# + assert( isdefined( level.fileprint_mapentcount ), "need to start a map with fileprint_map_start() first" ); + assert( !isdefined( level.fileprint_entitystart ) ); + level.fileprint_entitystart = true; + fileprint_launcher( "entity " + level.fileprint_mapentcount ); + fileprint_launcher( "{" ); + level.fileprint_mapentcount++; + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: fileprint_map_entity_end()" +"Summary: close brackets an entity, required for the next entity to begin" +"Module: Fileprint" +"CallOn: Level" +"Example: fileprint_map_entity_end();" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +fileprint_map_entity_end() +{ + /# + fileprint_launcher( "}" ); + level.fileprint_entitystart = undefined; + #/ +} + + + /* + ============= +///ScriptDocBegin +"Name: fileprint_radiant_vec( )" +"Summary: this converts a vector to a .map file readable format" +"Module: Fileprint" +"CallOn: An entity" +"MandatoryArg: : " +"Example: origin_string = fileprint_radiant_vec( vehicle.angles )" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +fileprint_radiant_vec( vector ) +{ + /# + string = "" + vector[ 0 ] + " " + vector[ 1 ] + " " + vector[ 2 ] + ""; + return string; + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: array_remove( , )" +"Summary: Returns < ents > array minus < remover > " +"Module: Array" +"CallOn: " +"MandatoryArg: : array to remove < remover > from" +"MandatoryArg: : entity to remove from the array" +"Example: ents = array_remove( ents, guy );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_remove( ents, remover ) +{ + newents = []; + foreach( ent in ents ) + { + if ( ent != remover ) + newents[ newents.size ] = ent; + } + + return newents; +} + +/* +============= +///ScriptDocBegin +"Name: array_remove_array( , )" +"Summary: Returns array minus any element in " +"Module: Array" +"MandatoryArg: : array to remove from" +"MandatoryArg: : array to remove from the " +"Example: ents = array_remove_array( ents, guys_to_remove );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +array_remove_array( ents, remover_array ) +{ + foreach( remover in remover_array ) + ents = array_remove( ents, remover ); + + return ents; +} + + /* + ============= +///ScriptDocBegin +"Name: array_removeUndefined( )" +"Summary: Returns a new array of < array > minus the undefined indicies" +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to search for undefined indicies in." +"Example: ents = array_removeUndefined( ents );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_removeUndefined( array ) +{ + newArray = []; + foreach ( i, item in array ) + { + if ( !IsDefined( item ) ) + continue; + newArray[ newArray.size ] = item; + } + return newArray; +} + + +/* +============= +///ScriptDocBegin +"Name: array_remove_duplicates( )" +"Summary: Returns array with unique entries. Ignores and consequently removes undefined entries." +"Module: Array" +"CallOn: " +"MandatoryArg: : Array to modify" +"Example: ai_left = array_remove_duplicates( ai_array );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +array_remove_duplicates( array ) +{ + array_unique = []; + + foreach ( item in array ) + { + if ( !IsDefined( item ) ) + continue; + + keep = true; + + foreach ( _item in array_unique ) + { + if ( item == _item ) + { + keep = false; + break; + } + } + + if ( keep ) + { + array_unique[ array_unique.size ] = item; + } + } + + return array_unique; +} + + /* + ============= +///ScriptDocBegin +"Name: array_levelthread( , , , , )" +"Summary: Threads the < process > function for every entity in the < entities > array. The level calls the function and each entity of the array is passed as the first parameter to the process." +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to thread the process" +"MandatoryArg: : pointer to a script function" +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"Example: array_levelthread( getentarray( "palm", "targetname" ), ::palmTrees );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_levelthread( array, process, var1, var2, var3 ) +{ + if ( isdefined( var3 ) ) + { + foreach ( ent in array ) + thread [[ process ]]( ent, var1, var2, var3 ); + + return; + } + + if ( isdefined( var2 ) ) + { + foreach ( ent in array ) + thread [[ process ]]( ent, var1, var2 ); + + return; + } + + if ( isdefined( var1 ) ) + { + foreach ( ent in array ) + thread [[ process ]]( ent, var1 ); + + return; + } + + foreach ( ent in array ) + thread [[ process ]]( ent ); +} + + /* + ============= +///ScriptDocBegin +"Name: array_levelcall( , , , , )" +"Summary: Calls the < process > function for every entity in the < entities > array. The level calls the function and each entity of the array is passed as the first parameter to the process." +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to thread the process" +"MandatoryArg: : pointer to a code function" +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"Example: array_levelthread( array_of_trees, ::palmTrees );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_levelcall( array, process, var1, var2, var3 ) +{ + if ( isdefined( var3 ) ) + { + foreach ( ent in array ) + call [[ process ]]( ent, var1, var2, var3 ); + + return; + } + + if ( isdefined( var2 ) ) + { + foreach ( ent in array ) + call [[ process ]]( ent, var1, var2 ); + + return; + } + + if ( isdefined( var1 ) ) + { + foreach ( ent in array ) + call [[ process ]]( ent, var1 ); + + return; + } + + foreach ( ent in array ) + call [[ process ]]( ent ); +} + +/* +============= +///ScriptDocBegin +"Name: add_to_array( , )" +"Summary: Adds < ent > to < array > and returns the new array." +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to add < ent > to." +"MandatoryArg: : The entity to be added." +"Example: nodes = add_to_array( nodes, new_node );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +add_to_array( array, ent ) +{ + if ( !isdefined( ent ) ) + return array; + + if ( !isdefined( array ) ) + array[ 0 ] = ent; + else + array[ array.size ] = ent; + + return array; +} + + + +/* +============= +///ScriptDocBegin +"Name: flag_assert( )" +"Summary: Asserts that a flag is clear. Useful for proving an assumption of a flag's state" +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : flag name" +"Example: flag_assert( "fairground_begins" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +flag_assert( msg ) +{ + assertEx( !flag( msg ), "Flag " + msg + " set too soon!" ); +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_either( , )" +"Summary: Waits until either of the the specified flags are set." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of one flag to wait on" +"MandatoryArg: : name of the other flag to wait on" +"Example: flag_wait( "hq_cleared", "hq_destroyed" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_either( flag1, flag2 ) +{ + for ( ;; ) + { + if ( flag( flag1 ) ) + return; + if ( flag( flag2 ) ) + return; + + level waittill_either( flag1, flag2 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_either_return( , )" +"Summary: Waits until either of the the specified flags are set, and returns the first one it found." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of one flag to wait on" +"MandatoryArg: : name of the other flag to wait on" +"Example: flag_wait_either_return( "hq_cleared", "hq_destroyed" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_either_return( flag1, flag2 ) +{ + for ( ;; ) + { + if ( flag( flag1 ) ) + return flag1; + if ( flag( flag2 ) ) + return flag2; + + msg = level waittill_any_return( flag1, flag2 ); + return msg; + } +} + + /* + ============= +///ScriptDocBegin +"Name: flag_waitopen_either( , )" +"Summary: Waits until either of the the specified flags are cleared." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of one flag to wait on" +"MandatoryArg: : name of the other flag to wait on" +"Example: flag_waitopen_either( "hq_cleared", "hq_destroyed" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +flag_waitopen_either( flag1, flag2 ) +{ + Assert( IsDefined( flag1 ) ); + Assert( IsDefined( flag2 ) ); + while( true ) + { + if( !flag( flag1 ) ) + return flag1; + + if( !flag( flag2 ) ) + return flag2; + + level waittill_either( flag1, flag2 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: flag_waitopen_either_return( , )" +"Summary: Waits until either of the the specified flags are cleared. Returns the flag that is first set." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of one flag to wait on" +"MandatoryArg: : name of the other flag to wait on" +"Example: hq_status = flag_waitopen_either_return( "hq_cleared", "hq_destroyed" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_waitopen_either_return( flag1, flag2 ) +{ + return flag_waitopen_either( flag1, flag2 ); +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_any( , , , , , )" +"Summary: Waits until any of the the specified flags are set." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of a flag to wait on" +"MandatoryArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"Example: flag_wait_any( "hq_cleared", "hq_destroyed", "hq_overrun", "hq_skipped" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_any( flag1, flag2, flag3, flag4, flag5, flag6 ) +{ + array = []; + if ( isdefined( flag6 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + array[ array.size ] = flag4; + array[ array.size ] = flag5; + array[ array.size ] = flag6; + } + else if ( isdefined( flag5 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + array[ array.size ] = flag4; + array[ array.size ] = flag5; + } + else if ( isdefined( flag4 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + array[ array.size ] = flag4; + } + else if ( isdefined( flag3 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + } + else if ( isdefined( flag2 ) ) + { + flag_wait_either( flag1, flag2 ); + return; + } + else + { + assertmsg( "flag_wait_any() needs at least 2 flags passed to it" ); + return; + } + + for ( ;; ) + { + for ( i = 0; i < array.size; i++ ) + { + if ( flag( array[ i ] ) ) + return; + } + + level waittill_any( flag1, flag2, flag3, flag4, flag5, flag6 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_any_return( , , , , )" +"Summary: Waits until any of the the specified flags are set, and returns the first set flag that was found." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of a flag to wait on" +"MandatoryArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"Example: returned = flag_wait_any_return( "hq_cleared", "hq_destroyed", "hq_overrun", "hq_skipped" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_any_return( flag1, flag2, flag3, flag4, flag5 ) +{ + array = []; + + if ( isdefined( flag5 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + array[ array.size ] = flag4; + array[ array.size ] = flag5; + } + else if ( isdefined( flag4 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + array[ array.size ] = flag4; + } + else if ( isdefined( flag3 ) ) + { + array[ array.size ] = flag1; + array[ array.size ] = flag2; + array[ array.size ] = flag3; + } + else if ( isdefined( flag2 ) ) + { + msg = flag_wait_either_return( flag1, flag2 ); + return msg; + } + else + { + assertmsg( "flag_wait_any_return() needs at least 2 flags passed to it" ); + return; + } + + for ( ;; ) + { + for ( i = 0; i < array.size; i++ ) + { + if ( flag( array[ i ] ) ) + return array[ i ]; + } + + msg = level waittill_any_return( flag1, flag2, flag3, flag4, flag5 ); + return msg; + } +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_all( , , , )" +"Summary: Waits until all of the the specified flags are set." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of a flag to wait on" +"MandatoryArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"OptionalArg: : name of a flag to wait on" +"Example: flag_wait_any( "hq_cleared", "hq_destroyed", "hq_overrun", "hq_skipped" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_all( flag1, flag2, flag3, flag4 ) +{ + if ( isdefined( flag1 ) ) + flag_wait( flag1 ); + + if ( isdefined( flag2 ) ) + flag_wait( flag2 ); + + if ( isdefined( flag3 ) ) + flag_wait( flag3 ); + + if ( isdefined( flag4 ) ) + flag_wait( flag4 ); +} + + /* + ============= +///ScriptDocBegin +"Name: flag_wait_or_timeout( , )" +"Summary: Waits until either the flag gets set or the timer elapses." +"Module: Flag" +"CallOn: " +"MandatoryArg: : Amount of time to wait before continuing regardless of flag." +"Example: flag_wait_or_timeout( "time_to_go", 3 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flag_wait_or_timeout( flagname, timer ) +{ + timerMS = timer * 1000; + start_time = GetTime(); + + for ( ;; ) + { + if ( flag( flagname ) ) + { + break; + } + + if ( GetTime() >= start_time + timerMS ) + { + break; + } + + timeRemaining = timerMS - ( GetTime() - start_time ); // figure out how long we waited already, if at all + timeRemainingSecs = timeRemaining / 1000; + wait_for_flag_or_time_elapses( flagname, timeRemainingSecs ); + } +} + +/* +============= +///ScriptDocBegin +"Name: flag_waitopen_or_timeout( , )" +"Summary: Waits until either the flag gets cleared or the timer elapses." +"Module: Flag" +"CallOn: " +"MandatoryArg: : Amount of time to wait before continuing regardless of flag." +"Example: flag_waitopen_or_timeout( "time_to_go", 3 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +flag_waitopen_or_timeout( flagname, timer ) +{ + start_time = gettime(); + for ( ;; ) + { + if ( !flag( flagname ) ) + break; + + if ( gettime() >= start_time + timer * 1000 ) + break; + + wait_for_flag_or_time_elapses( flagname, timer ); + } +} + +wait_for_flag_or_time_elapses( flagname, timer ) +{ + level endon( flagname ); + wait( timer ); +} + + + /* + ============= +///ScriptDocBegin +"Name: delayCall( , , , , )" +"Summary: delayCall is cool! It saves you from having to write extra script for once off commands. Note you don?t have to thread it off. delaycall is that smart!" +"Module: Utility" +"MandatoryArg: : The delay before the function occurs" +"MandatoryArg: : The function to run." +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"OptionalArg: : parameter 4 to pass to the process" +"OptionalArg: : parameter 5 to pass to the process" +"OptionalArg: : parameter 6 to pass to the process" +"OptionalArg: : parameter 7 to pass to the process" +"OptionalArg: : parameter 8 to pass to the process" +"Example: delayCall( 3, ::flag_set, "player_can_rappel" );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +delayCall( timer, func, param1, param2, param3, param4, param5, param6, param7, param8, param9 ) +{ + // to thread it off + thread delayCall_proc( func, timer, param1, param2, param3, param4, param5, param6, param7, param8, param9 ); +} + +delayCall_proc( func, timer, param1, param2, param3, param4, param5, param6, param7, param8, param9 ) +{ + if ( isSP() ) + { + self endon( "death" ); + self endon( "stop_delay_call" ); + } + + wait( timer ); + if ( isdefined( param9 ) ) + self call [[ func ]]( param1, param2, param3, param4, param5, param6, param7, param8, param9 ); + else + if ( isdefined( param8 ) ) + self call [[ func ]]( param1, param2, param3, param4, param5, param6, param7, param8 ); + else + if ( isdefined( param7 ) ) + self call [[ func ]]( param1, param2, param3, param4, param5, param6, param7 ); + else + if ( isdefined( param6 ) ) + self call [[ func ]]( param1, param2, param3, param4, param5, param6 ); + else + if ( isdefined( param5 ) ) + self call [[ func ]]( param1, param2, param3, param4, param5 ); + else + if ( isdefined( param4 ) ) + self call [[ func ]]( param1, param2, param3, param4 ); + else + if ( isdefined( param3 ) ) + self call [[ func ]]( param1, param2, param3 ); + else + if ( isdefined( param2 ) ) + self call [[ func ]]( param1, param2 ); + else + if ( isdefined( param1 ) ) + self call [[ func ]]( param1 ); + else + self call [[ func ]](); +} + + /* + ============= +///ScriptDocBegin +"Name: delay_script_call( , , , , )" +"Summary: delay_script_call() is just like delayCall(), except it works for script functions." +"Module: Utility" +"MandatoryArg: : The delay before the function occurs" +"MandatoryArg: : The function to run." +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"OptionalArg: : parameter 4 to pass to the process" +"OptionalArg: : parameter 5 to pass to the process" +"OptionalArg: : parameter 6 to pass to the process" +"OptionalArg: : parameter 7 to pass to the process" +"OptionalArg: : parameter 8 to pass to the process" +"OptionalArg: : parameter 8 to pass to the process" +"Example: delay_script_call( ::flag_set, "player_can_rappel", 3 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +delay_script_call( timer, func, param1, param2, param3, param4, param5, param6, param7, param8, param9 ) +{ + // to thread it off + thread delay_script_call_proc( func, timer, param1, param2, param3, param4, param5, param6, param7, param8, param9 ); +} + +// delay_script_call_proc() is just like delayCall_proc(), except it works for script functions. +delay_script_call_proc( func, timer, param1, param2, param3, param4, param5, param6, param7, param8, param9 ) +{ + wait( timer ); + if ( isdefined( param9 ) ) + self [[ func ]]( param1, param2, param3, param4, param5, param6, param7, param8, param9 ); + else if ( isdefined( param8 ) ) + self [[ func ]]( param1, param2, param3, param4, param5, param6, param7, param8 ); + else + if ( isdefined( param7 ) ) + self [[ func ]]( param1, param2, param3, param4, param5, param6, param7 ); + else + if ( isdefined( param6 ) ) + self [[ func ]]( param1, param2, param3, param4, param5, param6 ); + else + if ( isdefined( param5 ) ) + self [[ func ]]( param1, param2, param3, param4, param5 ); + else + if ( isdefined( param4 ) ) + self [[ func ]]( param1, param2, param3, param4 ); + else + if ( isdefined( param3 ) ) + self [[ func ]]( param1, param2, param3 ); + else + if ( isdefined( param2 ) ) + self [[ func ]]( param1, param2 ); + else + if ( isdefined( param1 ) ) + self [[ func ]]( param1 ); + else + self [[ func ]](); +} + + /* + ============= +///ScriptDocBegin +"Name: noself_delayCall( , , , , , )" +"Summary: Calls a command with no self (some commands don't support having self)." +"Module: Utility" +"MandatoryArg: : The delay before the function occurs" +"MandatoryArg: : The function to run." +"OptionalArg: : parameter 1 to pass to the process" +"OptionalArg: : parameter 2 to pass to the process" +"OptionalArg: : parameter 3 to pass to the process" +"OptionalArg: : parameter 4 to pass to the process" +"Example: noself_delayCall( ::setsaveddvar, "player_can_rappel", 1 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +noself_delayCall( timer, func, param1, param2, param3, param4 ) +{ + // to thread it off + thread noself_delayCall_proc( func, timer, param1, param2, param3, param4 ); +} + +noself_delayCall_proc( func, timer, param1, param2, param3, param4 ) +{ + wait( timer ); + if ( isdefined( param4 ) ) + call [[ func ]]( param1, param2, param3, param4 ); + else + if ( isdefined( param3 ) ) + call [[ func ]]( param1, param2, param3 ); + else + if ( isdefined( param2 ) ) + call [[ func ]]( param1, param2 ); + else + if ( isdefined( param1 ) ) + call [[ func ]]( param1 ); + else + call [[ func ]](); +} + + /* + ============= +///ScriptDocBegin +"Name: isSP()" +"Summary: Returns false if the level name begins with mp_" +"Module: Utility" +"Example: if ( isSP() );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +isSP() +{ + if ( !isdefined( level.isSP ) ) + level.isSP = !( string_starts_with( getdvar( "mapname" ), "mp_" ) ); + + return level.isSP; +} + + +/* + ============= +///ScriptDocBegin +"Name: isSP_TowerDefense()" +"Summary: Returns true if the level name begins with so_td_" +"Module: Utility" +"Example: if ( isSP_TowerDefense() );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +isSP_TowerDefense() +{ + if ( !isdefined( level.isSP_TowerDefense ) ) + level.isSP_TowerDefense = string_starts_with( getdvar( "mapname" ), "so_td_" ); + + return level.isSP_TowerDefense; +} + +/* +============= +///ScriptDocBegin +"Name: string_starts_with( , )" +"Summary: Returns true if the first string begins with the first string" +"Module: Utility" +"CallOn:" +"MandatoryArg: String to check" +"MandatoryArg: Beginning of string to check" +"Example: if ( string_starts_with( "somestring", "somest" ) )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +string_starts_with( string, start ) +{ + assert( isdefined( string ) ); + assert( isdefined( start ) ); + if ( string.size < start.size ) + return false; + + for ( i = 0 ; i < start.size ; i++ ) + { + if ( tolower( string[ i ] ) != tolower( start[ i ] ) ) + return false; + } + + return true; +} + +/* +============= +///ScriptDocBegin +"Name: string_find( , )" +"Summary: Returns index of the first occurance of strSearch within string or -1 if not found. Returns 0 if strSearch is empty." +"Module: Utility" +"CallOn:" +"MandatoryArg: String to check" +"MandatoryArg: sub string to check for" +"Example: if ( string_find( "somestring", "str" ) >= 0 )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +string_find( string, strSearch ) +{ + assert( IsString( string ) ); + assert( IsString( strSearch ) ); + + if ( string.size < strSearch.size ) + return -1; + + if ( strSearch.size == 0 ) + return 0; + + i = 0; + j = 0; + + while ( i < string.size ) + { + if ( tolower( string[ i ] ) == tolower( strSearch[ j ] ) ) + { + j++; + } + else + { + j = 0; + + if ( tolower( string[ i ] ) == tolower( strSearch[ j ] ) ) + { + j++; + } + else if ( i > (string.size - strSearch.size) ) + { + return -1; + } + } + + if ( j >= strSearch.size ) + return (i - j) + 1; + + i++; + } + + return -1; +} + +plot_points( plotpoints, r, g, b, timer ) +{ + lastpoint = plotpoints[ 0 ]; + if ( !isdefined( r ) ) + r = 1; + if ( !isdefined( g ) ) + g = 1; + if ( !isdefined( b ) ) + b = 1; + if ( !isdefined( timer ) ) + timer = 0.05; + for ( i = 1;i < plotpoints.size;i++ ) + { + thread draw_line_for_time( lastpoint, plotpoints[ i ], r, g, b, timer ); + lastpoint = plotpoints[ i ]; + } +} + + + /* + ============= +///ScriptDocBegin +"Name: draw_line_for_time( , , , , , , )" +"Summary: Draws a line from < org1 > to < org2 > in the specified color for the specified duration" +"Module: Debug" +"CallOn: " +"MandatoryArg: : starting origin for the line" +"MandatoryArg: : ending origin for the line" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : time in seconds the line should last" +"OptionalArg: : true if lines should test against z buffer, or false to draw on top of everything (default false)" +"Example: thread draw_line_for_time( level.player.origin, vehicle.origin, 1, 0, 0, 10.0 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +draw_line_for_time( org1, org2, r, g, b, timer, depthTest ) +{ + if ( !IsDefined( depthTest ) ) + depthTest = false; + + timer = gettime() + ( timer * 1000 ); + while ( gettime() < timer ) + { + line( org1, org2, ( r, g, b ), 1, depthTest ); + wait .05; + } + +} + +/* + ============= +///ScriptDocBegin +"Name: table_combine( , )" +"Summary: Combines the two tables and returns the resulting table with keys preserved. This function does not allow duplicate keys." +"Module: Array" +"CallOn: " +"MandatoryArg: : first table" +"MandatoryArg: : second table" +"Example: combinedTable = table_combine( table1, table2 );" +"SPMP: both" +///ScriptDocEnd + ============= +*/ +table_combine( table1, table2 ) +{ + table3 = []; + + foreach ( key, item in table1 ) + { + table3[key] = item; + } + + foreach ( key, item in table2 ) + { + assert( table3[key] == undefined ); + table3[key] = item; + } + return table3; +} + + + /* + ============= +///ScriptDocBegin +"Name: array_combine( , )" +"Summary: Combines the two arrays and returns the resulting array. This function doesn't care if it produces duplicates in the array." +"Module: Array" +"CallOn: " +"MandatoryArg: : first array" +"MandatoryArg: : second array" +"Example: combinedArray = array_combine( array1, array2 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_combine( array1, array2 ) +{ + array3 = []; + foreach ( item in array1 ) + { + if( IsDefined(item) ) + array3[ array3.size ] = item; + } + foreach ( item in array2 ) + { + if( IsDefined(item) ) + array3[ array3.size ] = item; + } + return array3; +} + + /* + ============= +///ScriptDocBegin +"Name: array_combine_non_integer_indices( , )" +"Summary: Combines the two arrays and returns the resulting array. This function doesn't care if it produces duplicates in the array." +"Summary: This is meant to be used on arrays with non-integer indices (guys_at["flag_a"] = 5, for example). All indices must be unique" +"Module: Array" +"CallOn: " +"MandatoryArg: : first array" +"MandatoryArg: : second array" +"Example: combinedArray = array_combine_non_integer_indices( array1, array2 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_combine_non_integer_indices( array1, array2 ) +{ + array3 = []; + foreach( index, item in array1 ) + { + Assert(!IsDefined(array3[index])); + array3[index] = item; + } + foreach( index, item in array2 ) + { + Assert(!IsDefined(array3[index])); + array3[index] = item; + } + return array3; +} + + /* + ============= +///ScriptDocBegin +"Name: array_randomize( )" +"Summary: Randomizes the array and returns the new array." +"Summary: Will not work on an array that does not have integer indices. For example: array["allies"] = 2" +"Module: Array" +"CallOn: " +"MandatoryArg: : Array to be randomized." +"Example: roof_nodes = array_randomize( roof_nodes );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_randomize( array ) +{ + for ( i = 0; i < array.size; i++ ) + { + j = RandomInt( array.size ); + temp = array[ i ]; + array[ i ] = array[ j ]; + array[ j ] = temp; + } + return array; +} + + +/* +============= +///ScriptDocBegin +"Name: array_add( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_add( array, ent ) +{ + array[ array.size ] = ent; + return array; +} + + /* + ============= +///ScriptDocBegin +"Name: array_first( " +"Summary: Returns first element regardless of keys" +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to add to." +"Example: firstAI = array_first( badguys);" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +array_first( array ) +{ + first = undefined; + foreach(value in array) + { + first = value; + break; + } + return first; +} + + /* + ============= +///ScriptDocBegin +"Name: array_insert( , , )" +"Summary: Returns a new array of < array > plus < object > at the specified index" +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to add to." +"MandatoryArg: : The entity to add" +"MandatoryArg: : The index position < object > should be added to." +"Example: ai = array_insert( ai, spawned, 0 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +array_insert( array, object, index ) +{ + if ( index == array.size ) + { + temp = array; + temp[ temp.size ] = object; + return temp; + } + temp = []; + offset = 0; + for ( i = 0; i < array.size; i++ ) + { + if ( i == index ) + { + temp[ i ] = object; + offset = 1; + } + temp[ i + offset ] = array[ i ]; + } + + return temp; +} + +/* +============= +///ScriptDocBegin +"Name: array_contains( , )" +"Summary: Checks whether an item is in the array or not." +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to search." +"MandatoryArg: : The item to see if it exists in the array" +"Example: if( array_contains( array, important_item ) )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +array_contains( array, compare ) +{ + if ( array.size <= 0 ) + return false; + + foreach ( member in array ) + { + if ( member == compare ) + return true; + } + + return false; +} + + /* + ============= +///ScriptDocBegin +"Name: array_find( , )" +"Summary: Searches for the first occurrence of item in array and returns the key. Returns undefined if not found" +"Module: Array" +"CallOn: " +"MandatoryArg: : array to search" +"MandatoryArg: : item to search for" +"Example: founditem = array_find( array, item );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_find( array, item ) +{ + foreach (idx, test in array) + { + if (test == item) + { + return idx; + } + } + return undefined; +} + + /* + ============= +///ScriptDocBegin +"Name: flat_angle( )" +"Summary: Returns the specified angle as a flat angle.( 45, 90, 30 ) becomes( 0, 90, 0 ). Useful if you just need an angle around Y - axis." +"Module: Vector" +"CallOn: " +"MandatoryArg: : angles to flatten" +"Example: yaw = flat_angle( node.angles );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flat_angle( angle ) +{ + rangle = ( 0, angle[ 1 ], 0 ); + return rangle; +} + + /* + ============= +///ScriptDocBegin +"Name: flat_origin( )" +"Summary: Returns a flat origin of the specified origin. Moves Z corrdinate to 0.( x, y, z ) becomes( x, y, 0 )" +"Module: Vector" +"CallOn: " +"MandatoryArg: : origin to flatten" +"Example: org = flat_origin( self.origin );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +flat_origin( org ) +{ + rorg = ( org[ 0 ], org[ 1 ], 0 ); + return rorg; + +} + + /* + ============= +///ScriptDocBegin +"Name: draw_arrow_time( , , , )" +"Summary: Draws an arrow pointing at < end > in the specified color for < duration > seconds." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : starting coordinate for the arrow" +"MandatoryArg: : ending coordinate for the arrow" +"MandatoryArg: :( r, g, b ) color array for the arrow" +"MandatoryArg: : time in seconds to draw the arrow" +"Example: thread draw_arrow_time( lasttarg.origin, targ.origin, ( 0, 0, 1 ), 5.0 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +draw_arrow_time( start, end, color, duration ) +{ + level endon( "newpath" ); + pts = []; + angles = vectortoangles( start - end ); + right = anglestoright( angles ); + forward = anglestoforward( angles ); + up = anglestoup( angles ); + + dist = distance( start, end ); + arrow = []; + range = 0.1; + + arrow[ 0 ] = start; + arrow[ 1 ] = start + ( right * ( dist * range ) ) + ( forward * ( dist * - 0.1 ) ); + arrow[ 2 ] = end; + arrow[ 3 ] = start + ( right * ( dist * ( -1 * range ) ) ) + ( forward * ( dist * - 0.1 ) ); + + arrow[ 4 ] = start; + arrow[ 5 ] = start + ( up * ( dist * range ) ) + ( forward * ( dist * - 0.1 ) ); + arrow[ 6 ] = end; + arrow[ 7 ] = start + ( up* (dist * ( -1 * range ) )) + ( forward*( dist * - 0.1) ); + arrow[ 8 ] = start; + + r = color[ 0 ]; + g = color[ 1 ]; + b = color[ 2 ]; + + plot_points( arrow, r, g, b, duration ); +} + + +/* +============= +///ScriptDocBegin +"Name: get_linked_ents()" +"Summary: Returns an array of entities that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to other entities" +"Example: spawners = heli get_linked_ents()" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_linked_ents() +{ + array = []; + + if ( isdefined( self.script_linkto ) ) + { + linknames = get_links(); + foreach ( name in linknames ) + { + entities = getentarray( name, "script_linkname" ); + if ( entities.size > 0 ) + array = array_combine( array, entities ); + } + } + + return array; +} + + +/* +============= +///ScriptDocBegin +"Name: get_linked_vehicle_nodes()" +"Summary: Returns an array of vehicle node that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to vehicle nodes" +"Example: nodes = tank get_linked_vehicle_nodes()" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_linked_vehicle_nodes() +{ + array = []; + + if ( IsDefined( self.script_linkto ) ) + { + linknames = get_links(); + foreach ( name in linknames ) + { + entities = GetVehicleNodeArray( name, "script_linkname" ); + if ( entities.size > 0 ) + array = array_combine( array, entities ); + } + } + return array; +} + + +/* +============= +///ScriptDocBegin +"Name: get_linked_ent()" +"Summary: Returns a single entity that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to another entity" +"Example: spawner = heli get_linked_ent()" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_linked_ent() +{ + array = get_linked_ents(); + assert( array.size == 1 ); + assert( isdefined( array[ 0 ] ) ); + return array[ 0 ]; +} + + +/* +============= +///ScriptDocBegin +"Name: get_linked_vehicle_node()" +"Summary: Returns a single vehicle node that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to one vehicle node" +"Example: node = tank get_linked_vehicle_node()" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_linked_vehicle_node() +{ + array = get_linked_vehicle_nodes(); + assert( array.size == 1 ); + assert( isdefined( array[ 0 ] ) ); + return array[ 0 ]; + +} + + +/* +============= +///ScriptDocBegin +"Name: get_links( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_links() +{ + return strtok( self.script_linkTo, " " ); +} + +/* +============= +///ScriptDocBegin +"Name: run_thread_on_targetname( , , , , )" +"Summary: Runs the specified thread on any entity with that targetname" +"Module: Utility" +"MandatoryArg: : The targetname" +"MandatoryArg: : The function" +"OptionalArg: : Optional argument" +"OptionalArg: : Optional argument" +"OptionalArg: : Optional argument" +"Example: run_thread_on_targetname( "chopper_guys", ::add_spawn_function, ::chopper_guys_land );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +run_thread_on_targetname( msg, func, param1, param2, param3 ) +{ + array = getentarray( msg, "targetname" ); + array_thread( array, func, param1, param2, param3 ); + + array = getstructarray( msg, "targetname" ); + array_thread( array, func, param1, param2, param3 ); + + array = call [[ level.getNodeArrayFunction ]]( msg, "targetname" ); + array_thread( array, func, param1, param2, param3 ); + + array = getvehiclenodearray( msg, "targetname" ); + array_thread( array, func, param1, param2, param3 ); +} + + +/* +============= +///ScriptDocBegin +"Name: run_thread_on_noteworthy( , , , , )" +"Summary: Runs the specified thread on any entity with that noteworthy" +"Module: Utility" +"MandatoryArg: : The noteworthy" +"MandatoryArg: : The function" +"OptionalArg: : Optional argument" +"OptionalArg: : Optional argument" +"OptionalArg: : Optional argument" +"Example: run_thread_on_noteworthy( "chopper_guys", ::add_spawn_function, ::chopper_guys_land );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + + +run_thread_on_noteworthy( msg, func, param1, param2, param3 ) +{ + array = getentarray( msg, "script_noteworthy" ); + array_thread( array, func, param1, param2, param3 ); + + array = getstructarray( msg, "script_noteworthy" ); + array_thread( array, func, param1, param2, param3 ); + + array = call [[ level.getNodeArrayFunction ]]( msg, "script_noteworthy" ); + array_thread( array, func, param1, param2, param3 ); + + array = getvehiclenodearray( msg, "script_noteworthy" ); + array_thread( array, func, param1, param2, param3 ); +} + + + /* + ============= +///ScriptDocBegin +"Name: draw_arrow( , , )" +"Summary: Draws an arrow pointing at < end > in the specified color for < duration > seconds." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : starting coordinate for the arrow" +"MandatoryArg: : ending coordinate for the arrow" +"MandatoryArg: :( r, g, b ) color array for the arrow" +"Example: draw_arrow( lasttarg.origin, targ.origin, ( 0, 0, 1 ));" +"SPMP: both" +///ScriptDocEnd + ============= + */ + +draw_arrow( start, end, color ) +{ + level endon( "newpath" ); + pts = []; + angles = vectortoangles( start - end ); + right = anglestoright( angles ); + forward = anglestoforward( angles ); + + dist = distance( start, end ); + arrow = []; + range = 0.05; + arrow[ 0 ] = start; + arrow[ 1 ] = start + ( right * ( dist * ( range ) ) ) + ( forward * ( dist * - 0.2 ) ); + arrow[ 2 ] = end; + arrow[ 3 ] = start + ( right * ( dist * ( -1 * range ) ) ) + ( forward * ( dist * - 0.2 ) ); + + for ( p = 0;p < 4;p++ ) + { + nextpoint = p + 1; + if ( nextpoint >= 4 ) + nextpoint = 0; + line( arrow[ p ], arrow[ nextpoint ], color, 1.0 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: draw_entity_bounds( , , , , )" +"Summary: Draws the bounding box of an entity." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : entity to draw bounding box around" +"MandatoryArg: : amount of time in seconds to draw the box" +"OptionalArg: :( r, g, b ) color array for the box, default is green" +"OptionalArg: : update the box if the entity moves - more expensive, default is false which assumes a static entity" +"OptionalArg: : amount of time to wait between dynamic updates, default is lowest 0.05 seconds - set higher if framerate suffers" +"Example: draw_entity_bounds( enemy, num_seconds, ( 0, 0, 1 ), true, 0.2 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +draw_entity_bounds( ent, time_sec, color, dynamic, dynamic_update_time_sec ) +{ + Assert( IsDefined( ent ) ); + Assert( time_sec > 0 ); + + if ( !IsDefined( color ) ) + color = ( 0, 1, 0 ); + + if ( !IsDefined( dynamic ) ) + dynamic = false; + + if ( !IsDefined( dynamic_update_time_sec ) ) + dynamic_update_time_sec = 0.05; + + if ( dynamic ) + num_frames = int( dynamic_update_time_sec / 0.05 ); + else + num_frames = int( time_sec / 0.05 ); + + points_side_1 = []; + points_side_2 = []; + + current = GetTime(); + end = current + ( time_sec * 1000 ); + while ( current < end && IsDefined( ent ) ) + { + points_side_1[0] = ent GetPointInBounds( 1, 1, 1 ); + points_side_1[1] = ent GetPointInBounds( 1, 1, -1 ); + points_side_1[2] = ent GetPointInBounds( -1, 1, -1 ); + points_side_1[3] = ent GetPointInBounds( -1, 1, 1 ); + + points_side_2[0] = ent GetPointInBounds( 1, -1, 1 ); + points_side_2[1] = ent GetPointInBounds( 1, -1, -1 ); + points_side_2[2] = ent GetPointInBounds( -1, -1, -1 ); + points_side_2[3] = ent GetPointInBounds( -1, -1, 1 ); + + for ( i = 0; i < 4; i++ ) + { + j = i + 1; + if ( j == 4 ) + j = 0; + + Line( points_side_1[i], points_side_1[j], color, 1, false, num_frames ); + Line( points_side_2[i], points_side_2[j], color, 1, false, num_frames ); + Line( points_side_1[i], points_side_2[i], color, 1, false, num_frames ); + } + + if ( !dynamic ) + return; + + wait dynamic_update_time_sec; + current = GetTime(); + } +} + +draw_volume( volume, time_sec, color, dynamic, dynamic_update_time_sec ) +{ + draw_entity_bounds( volume, time_sec, color, dynamic, dynamic_update_time_sec ); +} + +draw_trigger( trigger, time_sec, color, dynamic, dynamic_update_time_sec ) +{ + draw_entity_bounds( trigger, time_sec, color, dynamic, dynamic_update_time_sec ); +} + + + +/* +============= +///ScriptDocBegin +"Name: getfx( )" +"Summary: Gets the associated level._effect" +"Module: Utility" +"MandatoryArg: : The effect" +"Example: playfx ( getfx( "heli_dust_default" ), eOrgFx.origin + offset ); " +"SPMP: both" +///ScriptDocEnd +============= +*/ +getfx( fx ) +{ + assertEx( isdefined( level._effect[ fx ] ), "Fx " + fx + " is not defined in level._effect." ); + return level._effect[ fx ]; +} + +/* +============= +///ScriptDocBegin +"Name: fxExists( )" +"Summary: Returns whether or not an fx exists" +"Module: Utility" +"MandatoryArg: : The effect" +"Example: if ( fxExists( "blah" ) )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +fxExists( fx ) +{ + return isdefined( level._effect[ fx ] ); +} + +print_csv_asset( asset, type ) +{ + fileline = type + "," + asset; + if ( isdefined( level.csv_lines[ fileline ] ) ) + return; + level.csv_lines[ fileline ] = true; +// fileprint_chk( level.fileprint, fileline ); +} + +fileprint_csv_start( file ) +{ + /# + file = "scriptgen/" + file + ".csv"; + level.csv_lines = []; + #/ +} + + +/* +============= +///ScriptDocBegin +"Name: getLastWeapon( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +getLastWeapon() +{ + assert( isDefined( self.saved_lastWeapon ) ); + + return self.saved_lastWeapon; +} + + +/* +============= +///ScriptDocBegin +"Name: PlayerUnlimitedAmmoThread()" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +PlayerUnlimitedAmmoThread() +{ + /# + if ( !isdefined( self ) || self == level || self.code_classname != "player" ) + player = level.player; + else + player = self; + + assert( isdefined( player ) ); + + while ( 1 ) + { + wait .5; + + if ( getdvar( "UnlimitedAmmoOff" ) == "1" ) + continue; + + currentWeapon = player getCurrentWeapon(); + if ( currentWeapon != "none" ) + { + currentAmmo = player GetFractionMaxAmmo( currentWeapon ); + if ( currentAmmo < 0.2 ) + player GiveMaxAmmo( currentWeapon ); + } + currentTactical = player GetTacticalWeapon(); + if ( currentTactical != "none" ) + { + currentAmmo = player GetFractionMaxAmmo( currentTactical ); + if ( currentAmmo < 0.4 ) + player GiveMaxAmmo( currentTactical ); + } + currentLethal = player GetLethalWeapon(); + if ( currentLethal != "none" ) + { + currentAmmo = player GetFractionMaxAmmo( currentLethal ); + if ( currentAmmo < 0.4 ) + player GiveMaxAmmo( currentLethal ); + } + } + #/ +} + + +isUsabilityEnabled() +{ + return ( !self.disabledUsability ); +} + + +_disableUsability() +{ + if ( !IsDefined( self.disabledUsability ) ) + { + self.disabledUsability = 0; + } + + self.disabledUsability++; + self DisableUsability(); +} + + +_enableUsability() +{ + if ( !IsDefined( self.disabledUsability ) ) + { + self.disabledUsability = 0; + } + // when disabledUsability == 0, the player is enabled + // don't let the count ever go negative, because it doesn't make sense + else if ( self.disabledUsability > 0 ) + { + self.disabledUsability--; + + if ( self.disabledUsability == 0 ) + { + self EnableUsability(); +} + } +} + + +resetUsability() +{ + self.disabledUsability = 0; + self EnableUsability(); +} + + +_disableWeapon() +{ + if ( !IsDefined( self.disabledWeapon ) ) + { + self.disabledWeapon = 0; + } + + self.disabledWeapon++; + self disableWeapons(); +} + +_enableWeapon() +{ + if ( !IsDefined( self.disabledWeapon ) ) + { + self.disabledWeapon = 0; + } + + self.disabledWeapon--; + + assert( self.disabledWeapon >= 0 ); + + if ( !self.disabledWeapon ) + self enableWeapons(); +} + +isWeaponEnabled() +{ + return ( !self.disabledWeapon ); +} + + +_disableWeaponSwitch() +{ + if ( !IsDefined( self.disabledWeaponSwitch ) ) + { + self.disabledWeaponSwitch = 0; + } + + self.disabledWeaponSwitch++; + self disableWeaponSwitch(); +} + +_enableWeaponSwitch() +{ + if ( !IsDefined( self.disabledWeaponSwitch ) ) + { + self.disabledWeaponSwitch = 0; + } + + self.disabledWeaponSwitch--; + + assert( self.disabledWeaponSwitch >= 0 ); + + if ( !self.disabledWeaponSwitch ) + { + if( IsDefined( level.HordeWeaponsJammed) && level.HordeWeaponsJammed == true ) + return; + + else + self enableWeaponSwitch(); + } +} + +isWeaponSwitchEnabled() +{ + return ( !self.disabledWeaponSwitch ); +} + + +_disableOffhandWeapons() +{ + if ( !IsDefined( self.disabledOffhandWeapons ) ) + { + self.disabledOffhandWeapons = 0; + } + + self.disabledOffhandWeapons++; + self DisableOffhandWeapons(); +} + +_enableOffhandWeapons() +{ + if ( !IsDefined( self.disabledOffhandWeapons ) ) + { + self.disabledOffhandWeapons = 0; + } + + self.disabledOffhandWeapons--; + + assert( self.disabledOffhandWeapons >= 0 ); + + if ( !self.disabledOffhandWeapons ) + self EnableOffhandWeapons(); +} + +isOffhandWeaponEnabled() +{ + return ( !self.disabledOffhandWeapons ); +} + +_enableDetonate( weapon, enable ) +{ + if ( !self HasWeapon(weapon) ) + return; + + if ( self GetDetonateEnabled(weapon) == enable ) + return; + + self EnableDetonate( weapon, enable ); + + if ( enable ) + { + self notify("WeaponDetonateEnabled", weapon); + } + else + { + self notify("WeaponDetonateDisabled", weapon); + } +} + +/* +============= +///ScriptDocBegin +"Name: random( )" +"Summary: chose a random element of an array" +"Module: Array" +"CallOn: Level" +"MandatoryArg: : " +"Example: select_spot = random( array );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +random( array ) +{ + // process the array so it'll work with any string index arrays and arrays with missing entries. + newarray = []; + foreach ( index, value in array ) + { + newarray[ newarray.size ] = value; + } + + if ( !newarray.size ) + return undefined; + + return newarray[ randomint( newarray.size ) ]; +} + +/* +============= +///ScriptDocBegin +"Name: random_weight_sorted( )" +"Summary: chose a random element of an array tending to pick items closer to element 0" +"Module: Array" +"CallOn: Level" +"MandatoryArg: : " +"Example: random_node_near_me = random_weight_sorted( closest_nodes_sorted_by_distance );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +random_weight_sorted( array ) +{ + // process the array so it'll work with any string index arrays and arrays with missing entries. + newarray = []; + foreach ( index, value in array ) + { + newarray[ newarray.size ] = value; + } + + if ( !newarray.size ) + return undefined; + + rndSizeSq = randomint( newarray.size * newarray.size ); + return newarray[ ( newarray.size - 1 ) - int( sqrt( rndSizeSq ) ) ]; +} + +/* +============= +///ScriptDocBegin +"Name: spawn_tag_origin()" +"Summary: Spawn a script model with tag_origin model. If called on an entity, uses origin/angles of called on entity" +"Module: Utility" +"Example: ent = spawn_tag_origin();" +"SPMP: both" +///ScriptDocEnd +============= +*/ +spawn_tag_origin() +{ + tag_origin = spawn( "script_model", ( 0, 0, 0 ) ); + tag_origin setmodel( "tag_origin" ); + tag_origin hide(); + if ( isdefined( self.origin ) ) + tag_origin.origin = self.origin; + if ( isdefined( self.angles ) ) + tag_origin.angles = self.angles; + + return tag_origin; +} + + +/* +============= +///ScriptDocBegin +"Name: waittill_notify_or_timeout( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_notify_or_timeout( msg, timer ) +{ + self endon( msg ); + wait( timer ); +} + + +/* +============= +///ScriptDocBegin +"Name: waittill_notify_or_timeout_return( , )" +"Summary: returns the string 'timeout' if the timer elapses" +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : the notify to wait for" +"OptionalArg: : the amount of time in seconds to wait before timing out" +"Example: self waittill_notify_or_timeout_return( "death", 5.0 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +waittill_notify_or_timeout_return( msg, timer ) +{ + self endon( msg ); + wait( timer ); + return "timeout"; +} + + +/* +============= +///ScriptDocBegin +"Name: fileprint_launcher_start_file()" +"Summary: Tells Launcher to start storing text to a file. Use in conjunction with fileprint_launcher() and fileprint_launcher_end_file() to append to that file and then instruct launcher to write the file." +"Module: Print" +"CallOn: Level" +"Example: fileprint_launcher_start_file();" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +fileprint_launcher_start_file() +{ + AssertEx( ! isdefined( level.fileprint_launcher ), "Can't open more than one file at a time to print through launcher." ); + level.fileprintlauncher_linecount = 0; + level.fileprint_launcher = true; + fileprint_launcher( "GAMEPRINTSTARTFILE:" ); +} + +/* +============= +///ScriptDocBegin +"Name: fileprint_launcher( )" +"Summary: Tell launcher to append text to current open file created by fileprint_launcher_start_file(), to be closed and written with fileprint_launcher_end_file() " +"Module: Print" +"CallOn: Level" +"MandatoryArg: : " +"Example: fileprint_launcher( "main()" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +fileprint_launcher( string ) +{ + assert( isdefined( level.fileprintlauncher_linecount ) ); + level.fileprintlauncher_linecount++; + if( level.fileprintlauncher_linecount > 200 ) + { + wait .05; + level.fileprintlauncher_linecount = 0; + } + println( "LAUNCHERPRINTLN:" + string ); +} + + +/* +============= +///ScriptDocBegin +"Name: fileprint_launcher_end_file( , )" +"Summary: Tell launcher to write out Text that has been started and appended to using fileprint_launcher_start_file() and fileprint_launcher(). you must end a file before you can start a new one." +"Module: Print" +"CallOn: Level" +"MandatoryArg: : relative to game ( c:\trees\iw5\game )" +"OptionalArg: : Enabled will tell Perforce to check in the file." +"Example: fileprint_launcher_end_file( "\\share\\raw\\maps\\createart\\\" + level.script + "_art.gsc, true );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +fileprint_launcher_end_file( file_relative_to_game, bIsPerforceEnabled ) +{ + if( !isdefined( bIsPerforceEnabled ) ) + bIsPerforceEnabled = false; + + setDevDvarIfUninitialized("LAUNCHER_PRINT_FAIL", "0"); + setDevDvarIfUninitialized("LAUNCHER_PRINT_SUCCESS", "0"); + + if( bIsPerforceEnabled ) + fileprint_launcher( "GAMEPRINTENDFILE:GAMEPRINTP4ENABLED:"+file_relative_to_game ); + else + fileprint_launcher( "GAMEPRINTENDFILE:"+file_relative_to_game ); + + // wait for launcher to tell us that it's done writing the file + TimeOut = gettime()+4000; // give launcher 4 seconds to print the file. + while( getdvarint( "LAUNCHER_PRINT_SUCCESS" ) == 0 && getdvar( "LAUNCHER_PRINT_FAIL" ) == "0" && gettime() < TimeOut ) + wait .05; + + if( ! ( gettime() < TimeOut ) ) + { + iprintlnbold("LAUNCHER_PRINT_FAIL:( TIMEOUT ): launcherconflict? restart launcher and try again? " ); + setdevdvar("LAUNCHER_PRINT_FAIL", "0"); + level.fileprint_launcher = undefined; + return false; + } + + failvar = getdvar("LAUNCHER_PRINT_FAIL"); + if( failvar != "0" ) + { + iprintlnbold("LAUNCHER_PRINT_FAIL:( "+ failvar + " ): launcherconflict? restart launcher and try again? " ); + setdevdvar("LAUNCHER_PRINT_FAIL", "0"); + level.fileprint_launcher = undefined; + return false; + } + + setdevdvar("LAUNCHER_PRINT_FAIL", "0"); + setdevdvar( "LAUNCHER_PRINT_SUCCESS", "0" ); + + level.fileprint_launcher = undefined; + return true; +} + +/* +============= +///ScriptDocBegin +"Name: launcher_write_clipboard( )" +"Summary: send a string to your Connected PC's clipboard through launcher" +"Module: Print" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: launcher_write_clipboard( Players_origin_string )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +launcher_write_clipboard( str ) +{ + level.fileprintlauncher_linecount = 0; + fileprint_launcher( "LAUNCHER_CLIP:" + str ); +} + +/* +============= +///ScriptDocBegin +"Name: isDestructible()" +"Summary: returns true if self is a destructible" +"Module: Entity" +"CallOn: An entity" +"Example: if ( self isDestructible() )" +"SPMP: both" +///ScriptDocEnd +============= +*/ +isDestructible() +{ + if ( !isdefined( self ) ) + return false; + return isdefined( self.destructible_type ); +} + +/* +============= +///ScriptDocBegin +"Name: pauseEffect( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +pauseEffect() +{ + common_scripts\_createfx::stop_fx_looper(); +} + +/* +============= +///ScriptDocBegin +"Name: activate_individual_exploder()" +"Summary: Activates an individual exploder, rather than all the exploders of a given number" +"Module: Utility" +"CallOn: An exploder" +"Example: exploder activate_individual_exploder();" +"SPMP: both" +///ScriptDocEnd +============= +*/ +activate_individual_exploder() +{ + common_scripts\_exploder::activate_individual_exploder_proc(); +} + + +/* +============= +///ScriptDocBegin +"Name: get_target_ent( )" +"Summary: Returns whatever SINGLE ent is targetted, be it node, struct, or entity" +"Module: Utility" +"OptionalArg: : Optional target override" +"Example: node = guy get_target_ent();" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_target_ent( target ) +{ + if ( !isdefined( target ) ) + target = self.target; + + AssertEx( IsDefined( target ), "Self had no target!" ); + + ent = GetEnt( target, "targetname" ); + if ( IsDefined( ent ) ) + return ent; + + if ( isSP() ) + { + ent = call [[ level.getNodeFunction ]]( target, "targetname" ); + if ( IsDefined( ent ) ) + return ent; + } + + ent = getstruct( target, "targetname" ); + if ( IsDefined( ent ) ) + return ent; + + ent = GetVehicleNode( target, "targetname" ); + if ( IsDefined( ent ) ) + return ent; + + AssertMsg( "Tried to get ent " + target + ", but there was no ent." ); +} + +/* +============= +///ScriptDocBegin +"Name: get_noteworthy_ent( )" +"Summary: Returns whatever SINGLE ent is targetted, be it node, struct, or entity" +"Module: Utility" +"RequiredArg: : script_noteworthy" +"Example: node = get_noteworthy_ent( "special_node" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +get_noteworthy_ent( noteworthy ) +{ + AssertEx( IsDefined( noteworthy ), "No script_noteworthy provided!" ); + + ent = GetEnt( noteworthy, "script_noteworthy" ); + if ( IsDefined( ent ) ) + return ent; + + if ( isSP() ) + { + ent = call [[ level.getNodeFunction ]]( noteworthy, "script_noteworthy" ); + if ( IsDefined( ent ) ) + return ent; + } + + ent = getstruct( noteworthy, "script_noteworthy" ); + if ( IsDefined( ent ) ) + return ent; + + ent = GetVehicleNode( noteworthy, "script_noteworthy" ); + if ( IsDefined( ent ) ) + return ent; + + AssertEx( "Tried to get ent, but there was no ent." ); +} + +/* +============= +///ScriptDocBegin +"Name: do_earthquake( , )" +"Summary: play an earthquake that is defined by add_earthquake() " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +do_earthquake( name, origin ) +{ + eq = level.earthquake[ name ]; + Earthquake( eq[ "magnitude" ], eq[ "duration" ], origin, eq[ "radius" ] ); +} + +/* + ============= +///ScriptDocBegin +"Name: play_loopsound_in_space( , , )" +"Summary: Use the PlayLoopSound command at a position in space. Unrelated to caller." +"Module: Sound" +"CallOn: Level" +"MandatoryArg: : Sound alias to play" +"MandatoryArg: : Origin of the sound" +"Example: play_loopsound_in_space( "siren", level.speaker.origin );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +play_loopsound_in_space( alias, origin ) +{ + if ( !SoundExists( alias ) ) + { + println( "Warning: play_loopsound_in_space() alias doesn't exist: " + alias ); + return; + } + + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + if ( !isdefined( origin ) ) + origin = self.origin; + + org.origin = origin; + + org PlayLoopSound( alias ); + return org; +} + +/* + ============= +///ScriptDocBegin +"Name: play_sound_in_space_with_angles( , , , , )" +"Summary: Play a sound at an origin, unrelated to caller" +"Module: Sound" +"CallOn: Level" +"MandatoryArg: : Sound alias to play" +"MandatoryArg: : Origin of the sound" +"MandatoryArg: : Orientation of the sound" +"OptionalArg: : Play this sound as a master sound. Defaults to false" +"OptionalArg: : Shows a visual represntation for the sound" +"Example: play_sound_in_space_with_angles( "siren", level.speaker.origin, level.speaker.angles );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_sound_in_space_with_angles( alias, origin, angles, master ) +{ + if ( !SoundExists( alias ) ) + { + /# + iprintln( "alias " + alias + " doesn't exist." ); + #/ + return; + } + + org = Spawn( "script_origin", ( 0, 0, 1 ) ); + if ( !isdefined( origin ) ) + origin = self.origin; + org.origin = origin; + org.angles = angles; + if ( isSP() ) + { + if ( IsDefined( master ) && master ) + org PlaySoundAsMaster( alias, "sounddone" ); + else + org PlaySound( alias, "sounddone" ); + org waittill( "sounddone" ); + } + else + { + if ( IsDefined( master ) && master ) + org PlaySoundAsMaster( alias ); + else + org PlaySound( alias ); + } + org Delete(); +} + +/* + ============= +///ScriptDocBegin +"Name: play_sound_in_space( , , )" +"Summary: Play a sound at an origin, unrelated to caller" +"Module: Sound" +"CallOn: Level" +"MandatoryArg: : Sound alias to play" +"MandatoryArg: : Origin of the sound" +"OptionalArg: : Play this sound as a master sound. Defaults to false" +"Example: play_sound_in_space( "siren", level.speaker.origin );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_sound_in_space( alias, origin, master ) +{ + play_sound_in_space_with_angles( alias, origin, (0, 0, 0), master ); +} + + + +loop_fx_sound( alias, origin, culled, ender, createfx_ent ) +{ + if ( !SoundExists( alias ) ) + { + println( "Warning: loop_fx_sound() alias doesn't exist: " + alias ); + return; + } + + // If we have an ender specified clear the culled flag because server-culled sounds can't be stopped. + if ( IsDefined( ender ) ) + { + culled = undefined; + } + + if ( IsDefined( culled ) && culled && ( !IsDefined( level.first_frame ) || level.first_frame == 1 ) ) + { + SpawnLoopingSound( alias, origin, (0,0,0) ); + } + else + { + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + + if ( IsDefined( ender ) ) + { + thread loop_sound_delete( ender, org ); + self endon( ender ); + } + + org.origin = origin; + org PlayLoopSound( alias ); + org willNeverChange(); + } +} + +loop_fx_sound_with_angles( alias, origin, angles, culled, ender, createfx_ent, shape ) +{ + if ( !SoundExists( alias ) ) + { + println( "Warning: loop_fx_sound_with_angles() alias doesn't exist: " + alias ); + return; + } + + if ( IsDefined( culled ) && culled ) + { + // this is conflicting with restart fx looper. so check for first frame. + //its clear the design of this is meant to never be stopped. we don't need to restart - Nate + //I think some of this might be piggy backed on normal fx too, in which case we want to allow the effect to restart and not the sound. + + if ( !IsDefined( level.first_frame ) || level.first_frame == 1 ) + { + SpawnLoopingSound( alias, origin, angles ); + } + } + else + { + if ( IsDefined( level.createFX_enabled ) && level.createFX_enabled && IsDefined( createfx_ent.loopsound_ent ) ) + { + org = createfx_ent.loopsound_ent; + } + else + { + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + } + + if ( IsDefined( ender ) ) + { + thread loop_sound_delete( ender, org ); + self endon( ender ); + } + + org.origin = origin; + org.angles = angles; + org PlayLoopSound( alias ); + + if ( IsDefined( level.createFX_enabled ) && level.createFX_enabled ) + { + createfx_ent.loopsound_ent = org; + } + else + { + org willNeverChange(); + } + } +} + +loop_fx_sound_interval( alias, origin, ender, timeout, delay_min, delay_max ) +{ + loop_fx_sound_interval_with_angles( alias, origin, (0, 0, 0), ender, timeout, delay_min, delay_max ); +} + +loop_fx_sound_interval_with_angles( alias, origin, angles, ender, timeout, delay_min, delay_max ) +{ + + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + + if ( IsDefined( ender ) ) + { + thread loop_sound_delete( ender, org ); + self endon( ender ); + } + + org.origin = origin; + org.angles = angles; + + if( delay_min >= delay_max ) + { + while( true ) + { + Print3d( origin, "delay_min >= delay_max", (1,0,0), 1, 1); + wait .05; + } + } + + if ( !SoundExists( alias ) ) + { + while( true ) + { + Print3d( origin, "no sound: "+ alias, (1,0,0), 1, 1); + wait .05; + } + } + + while( true ) + { + wait RandomFloatRange( delay_min, delay_max ); + lock( "createfx_looper" ); + thread play_sound_in_space_with_angles( alias, org.origin, org.angles, undefined ); + unlock( "createfx_looper" ); + } +} + +loop_sound_delete( ender, ent ) +{ + ent endon( "death" ); + self waittill( ender ); + ent Delete(); +} + +/* +============= +///ScriptDocBegin +"Name: createLoopEffect( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +createLoopEffect( fxid ) +{ + ent = common_scripts\_createfx::createEffect( "loopfx", fxid ); + ent.v[ "delay" ] = common_scripts\_createfx::getLoopEffectDelayDefault(); + return ent; +} + +/* +============= +///ScriptDocBegin +"Name: createOneshotEffect( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +createOneshotEffect( fxid ) +{ + // uses triggerfx + ent = common_scripts\_createfx::createEffect( "oneshotfx", fxid ); + ent.v[ "delay" ] = common_scripts\_createfx::getOneshotEffectDelayDefault(); + return ent; +} + +/* +============= +///ScriptDocBegin +"Name: createExploder( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +createExploder( fxid ) +{ + ent = common_scripts\_createfx::createEffect( "exploder", fxid ); + ent.v[ "delay" ] = common_scripts\_createfx::getExploderDelayDefault(); + ent.v[ "exploder_type" ] = "normal"; + return ent; +} + +/* +============= +///ScriptDocBegin +"Name: alphabetize( )" +"Summary: " +"Module: Array" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ +alphabetize( array ) +{ + if ( array.size <= 1 ) + return array; + + count = 0; + for ( asize = array.size - 1; asize >= 1; asize-- ) + { + largest = array[asize]; + largestIndex = asize; + for ( i = 0; i < asize; i++ ) + { + string1 = array[ i ]; + + if ( StrICmp(string1, largest ) > 0 ) + { + largest = string1; + largestIndex = i; + } + } + + if(largestIndex != asize) + { + array[largestIndex] = array[asize]; + array[asize] = largest; + } + } + + return array; +} + +is_later_in_alphabet( string1, string2 ) +{ + return StrICmp( string1, string2 ) > 0; +} + +/* +============= +///ScriptDocBegin +"Name: play_loop_sound_on_entity( , )" +"Summary: Play loop sound alias on an entity" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to loop" +"OptionalArg: : Offset for sound origin relative to the world from the models origin." +"Example: vehicle thread play_loop_sound_on_entity( "engine_belt_run" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +play_loop_sound_on_entity( alias, offset ) +{ + if ( !SoundExists( alias ) ) + { + println( "Warning: play_loop_sound_on_entity() alias doesn't exist: " + alias ); + return; + } + + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + org endon( "death" ); + thread delete_on_death( org ); + + if ( IsDefined( offset ) ) + { + org.origin = self.origin + offset; + org.angles = self.angles; + org linktosynchronizedparent( self ); + } + else + { + org.origin = self.origin; + org.angles = self.angles; + org linktosynchronizedparent( self ); + } + +// org endon( "death" ); + org PlayLoopSound( alias ); +// PrintLn( "playing loop sound ", alias, " on entity at origin ", self.origin, " at ORIGIN ", org.origin ); + + self waittill( "stop sound" + alias ); + org StopLoopSound( alias ); + org Delete(); +} + +/* +============= +///ScriptDocBegin +"Name: stop_loop_sound_on_entity( )" +"Summary: Stop playing the the loop sound alias on an entity" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to stop looping" +"Example: vehicle thread stop_loop_sound_on_entity( "engine_belt_run" );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +stop_loop_sound_on_entity( alias ) +{ + self notify( "stop sound" + alias ); +} + +/* +============= +///ScriptDocBegin +"Name: delete_on_death( )" +"Summary: Delete the entity when "self" dies." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: level.helicopter thread delete_on_death( someRandomScriptOriginThatISpawned );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +delete_on_death( ent ) +{ + //self ==> the entity you want to wait to die before deleting the ent + ent endon( "death" ); + self waittill( "death" ); + if ( IsDefined( ent ) ) + ent Delete(); +} + +error( msg ) +{ + PrintLn( "^c * ERROR * ", msg ); + waitframe(); + + /# + if ( GetDvar( "debug" ) != "1" ) + AssertMsg( "This is a forced error - attach the log file. \n" + msg ); + #/ +} + + +/* +============= +///ScriptDocBegin +"Name: create_dvar( , )" +"Summary: Initialize a dvar with a given value" +"Module: Utility" +"MandatoryArg: : Name of the dvar" +"MandatoryArg: : Default value" +"Example: create_dvar( "fish", "on" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +create_dvar( var, val ) +{ + SetDvarIfUninitialized( var, val ); +} + + +void( arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13 ) +{ +} + + +/* +============= +///ScriptDocBegin +"Name: tag_project( , )" +"Summary: returns a point projected off a tag" +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: target = tank tag_project( "tag_flash", 99999 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +tag_project( tagname, dist ) +{ + org = self GetTagOrigin( tagname ); + angle = self GetTagAngles( tagname ); + vector = AnglesToForward( angle ); + vector = VectorNormalize( vector ) * dist; + return org + vector; +} + +/* +============= +///ScriptDocBegin +"Name: ter_op( , , )" +"Summary: Functon that serves as a tertiary operator in C/C++" +"Module: Utility" +"CallOn: " +"MandatoryArg: : The statement to evaluate" +"MandatoryArg: : The value that is returned when the statement evaluates to true" +"MandatoryArg: : That value that is returned when the statement evaluates to false" +"Example: x = ter_op( x > 5, 2, 7 );" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +ter_op( statement, true_value, false_value ) +{ + if ( statement ) + return true_value; + return false_value; +} + + +create_lock( msg, count ) +{ + if ( !IsDefined( count ) ) + count = 1; + + Assert( IsDefined( msg ) ); + + if ( !IsDefined( level.lock ) ) + level.lock = []; + + lock_struct = SpawnStruct(); + lock_struct.max_count = count; + lock_struct.count = 0; + level.lock[ msg ] = lock_struct; +} + +lock_exists( msg ) +{ + if( !IsDefined( level.lock ) ) + return false; + return IsDefined( level.lock[ msg ] ); +} + +lock( msg ) +{ + Assert( IsDefined( level.lock ) ); + Assert( IsDefined( level.lock[ msg ] ) ); + lock = level.lock[ msg ]; + while ( lock.count >= lock.max_count ) + lock waittill ( "unlocked" ); + lock.count++; +} + +is_locked( msg ) +{ + Assert( IsDefined( level.lock ) ); + Assert( IsDefined( level.lock[ msg ] ) ); + lock = level.lock[ msg ]; + return lock.count > lock.max_count; +} + +unlock_wait( msg ) +{ + //dodge endon issues + thread unlock_thread( msg ); + wait 0.05; +} + +unlock( msg ) +{ + //dodge endon issues + thread unlock_thread( msg ); +} + +unlock_thread( msg ) +{ + wait 0.05; + Assert( IsDefined( level.lock ) ); + Assert( IsDefined( level.lock[ msg ] ) ); + lock = level.lock[ msg ]; + lock.count--; + Assert( lock.count >= 0 ); + lock notify ( "unlocked" ); +} + +/* +============= +///ScriptDocBegin +"Name: get_template_level()" +"Summary: returns the templated level or level.script" +"Module: Entity" +"CallOn: An entity" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +get_template_level() +{ + script = level.script; + if ( IsDefined( level.template_script ) ) + script = level.template_script; + return script; +} + + /* + ============= +///ScriptDocBegin +"Name: array_reverse( )" +"Summary: Reverses the order of the array and returns the new array." +"Module: Array" +"CallOn: " +"MandatoryArg: : Array to be reversed." +"Example: patrol_nodes = array_reverse( patrol_nodes );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +array_reverse( array ) +{ + array2 = []; + for ( i = array.size - 1; i >= 0; i-- ) + array2[ array2.size ] = array[ i ]; + return array2; +} + + +/* +============= +///ScriptDocBegin +"Name: distance_2d_squared( , )" +"Summary: Returns the distance squared between vectors a and b in the x and y axis (z is ignored)." +"Module: Distance" +"MandatoryArg: : Vector of first position to be checked for distance 2d sqaured." +"MandatoryArg: : Vector of second position to be checked for distance 2d squared." +"Example: dist_2d_sqrd = distance_2d_squared( level.player.origin, helicopter.origin ) " +"SPMP: both" +///ScriptDocEnd +============= +*/ +distance_2d_squared( a, b ) +{ + return Length2DSquared( a - b ); +} + + /* + ============= +///ScriptDocBegin +"Name: get_array_of_farthest( , , , , , )" +"Summary: Returns an array of all the entities in < array > sorted in order of farthest to closest." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be farthest from." +"MandatoryArg: : Array of entities to check distance on." +"OptionalArg: : Array of entities to exclude from the check." +"OptionalArg: : Max size of the array to return" +"OptionalArg: : Max distance from the origin to return acceptable entities" +"OptionalArg: : Min distance from the origin to return acceptable entities" +"Example: allies_sort = get_array_of_closest( originFC1.origin, allies );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +get_array_of_farthest( org, array, excluders, max, maxdist, mindist ) +{ + aArray = get_array_of_closest( org, array, excluders, max, maxdist, mindist ); + aArray = array_reverse( aArray ); + return aArray; +} + + + + /* + ============= +///ScriptDocBegin +"Name: get_array_of_closest( , , , , , )" +"Summary: Returns an array of all the entities in < array > sorted in order of closest to farthest." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on." +"OptionalArg: : Array of entities to exclude from the check." +"OptionalArg: : Max size of the array to return" +"OptionalArg: : Max distance from the origin to return acceptable entities" +"OptionalArg: : Min distance from the origin to return acceptable entities" +"Example: allies_sort = get_array_of_closest( originFC1.origin, allies );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +get_array_of_closest( org, array, excluders, max, maxdist, mindist ) +{ + // pass an array of entities to this function and it will return them in the order of closest + // to the origin you pass, you can also set max to limit how many ents get returned + if ( !isdefined( max ) ) + max = array.size; + if ( !isdefined( excluders ) ) + excluders = []; + + maxdist2rd = undefined; + if ( IsDefined( maxdist ) ) + maxdist2rd = maxdist * maxdist; + + mindist2rd = 0; + if ( IsDefined( mindist ) ) + mindist2rd = mindist * mindist; + + // optimize the common case of a simple sort + if ( excluders.size == 0 && max >= array.size && mindist2rd == 0 && !isdefined( maxdist2rd ) ) + return SortByDistance( array, org ); + + newArray = []; + foreach ( ent in array ) + { + excluded = false; + foreach ( excluder in excluders ) + { + if ( ent == excluder ) + { + excluded = true; + break; + } + } + if ( excluded ) + continue; + + dist2rd = DistanceSquared( org, ent.origin ); + + if ( IsDefined( maxdist2rd ) && dist2rd > maxdist2rd ) + continue; + + if ( dist2rd < mindist2rd ) + continue; + + newArray[ newArray.size ] = ent; + } + + newArray = SortByDistance( newArray, org ); + + if ( max >= newArray.size ) + return newArray; + + finalArray = []; + for ( i = 0; i < max; i++ ) + finalArray[ i ] = newArray[ i ]; + + return finalArray; +} + + +/* +============= +///ScriptDocBegin +"Name: is_player_gamepad_enabled()" +"Summary: returns whether the player is using a gamepad" +"Module: Utility" +"CallOn: Player" +"Example: player is_player_gamepad_enabled()" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +is_player_gamepad_enabled() +{ + if ( !level.Console ) + { + player_gpad_enabled = self UsingGamepad(); + if ( IsDefined( player_gpad_enabled ) ) + { + return player_gpad_enabled; + } + else + { + return false; + } + } + + return true; +} + +/* +============= +///ScriptDocBegin +"Name: drop_to_ground( )" +"Summary: Return the ground point for this origin" +"Module: Utility" +"MandatoryArg: : The origin you want to find the ground point for" +"OptionalArg: : Optional height to drop the point from" +"OptionalArg: : Optional height to drop the point to" +"Example: ground_org = drop_to_ground( origin );" +"SPMP: both" +///ScriptDocEnd +============= +*/ +drop_to_ground( pos, updist, downdist ) +{ + if ( !isdefined( updist ) ) + updist = 1500; + if ( !isdefined( downdist ) ) + downdist = -12000; + + return PhysicsTrace( pos + ( 0, 0, updist ), pos + ( 0, 0, downdist ) ); +} + +add_destructible_type_function( destructible_type, function ) +{ + if ( !IsDefined( level.destructible_functions ) ) + level.destructible_functions = []; + + Assert( !IsDefined( level.destructible_functions[ destructible_type ] ) ); + + level.destructible_functions[ destructible_type ] = function; +} + +add_destructible_type_transient( destructible_type, name ) +{ + AssertEx( !IsDefined( level._loadStarted ), "add_destructible_type_transient() must be set before _load::main()" ); + + if ( !IsDefined( level.destructible_transient ) ) + level.destructible_transient = []; + + Assert( !IsDefined( level.destructible_transient[ destructible_type ] ) ); + + + level.destructible_transient[ destructible_type ] = name; +} + + /* + ============= +///ScriptDocBegin +"Name: within_fov( , , , )" +"Summary: Returns true if < end_origin > is within the field of view, otherwise returns false." +"Module: Vector" +"MandatoryArg: : starting origin for FOV check( usually the players origin )" +"MandatoryArg: : angles to specify facing direction( usually the players angles )" +"MandatoryArg: : origin to check if it's in the FOV" +"MandatoryArg: : cosine of the FOV angle to use" +"Example: qBool = within_fov( level.player.origin, level.player.angles, target1.origin, Cos( 45 ) );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +within_fov( start_origin, start_angles, end_origin, fov ) +{ + normal = VectorNormalize( end_origin - start_origin ); + forward = AnglesToForward( start_angles ); + dot = VectorDot( forward, normal ); + + return dot >= fov; +} + + + /* + ============= +///ScriptDocBegin +"Name: entity_path_disconnect_thread( )" +"Summary: Periodically updates path disconnects that are caused by this entity as it moves" +"Module: Utility" +"CallOn: entity" +"MandatoryArg: : how long the thread waits between updates" +"Example: crate thread entity_path_disconnect_thread( 1.0 );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +entity_path_disconnect_thread( updateRate ) +{ + self notify("entity_path_disconnect_thread"); + self endon("entity_path_disconnect_thread"); + + self endon("death"); + level endon("game_ended"); + + disconnect = false; + self.forceDisconnectUntil = 0; + + assert( updateRate >= 0.05 ); + + while(1) + { + lastPos = self.origin; + + event = waittill_any_timeout( updateRate, "path_disconnect" ); + + newDisconnect = false; + moved = DistanceSquared( self.origin, lastPos ) > 0; + + if ( moved ) + newDisconnect = true; + + if ( IsDefined( event ) && (event == "path_disconnect") ) + newDisconnect = true; + + if ( GetTime() < self.forceDisconnectUntil ) + newDisconnect = true; + + foreach ( character in level.characters ) + { + if ( IsAI( character ) && DistanceSquared( self.origin, character.origin ) < 500 * 500 ) + { + newDisconnect = true; + self.forceDisconnectUntil = max(GetTime() + 30000,self.forceDisconnectUntil); + } + } + + if ( (newDisconnect != disconnect) || moved ) + { + if ( newDisconnect ) + self DisconnectPaths(); + else + self ConnectPaths(); + + disconnect = newDisconnect; + } + } +} + + /* + ============= +///ScriptDocBegin +"Name: make_entity_sentient_mp( , )" +"Summary: Make an entity a sentient. Returns boolean result. Only runs if in MP and bots are enabled" +"Module: Utility" +"CallOn: entity" +"MandatoryArg: sentient team." +"OptionalArg: true if the sentient can be freed up to make room for other sentients +"Example: chopper make_entity_sentient_mp( heli_team );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +make_entity_sentient_mp( team, expendable ) +{ + if ( level.gameType == "aliens" && IsDefined( level.aliens_make_entity_sentient_func ) ) + return self [[ level.aliens_make_entity_sentient_func ]]( team, expendable ); + + if ( IsDefined( level.bot_funcs ) && IsDefined( level.bot_funcs["bots_make_entity_sentient"] ) ) + return self [[ level.bot_funcs["bots_make_entity_sentient"] ]]( team, expendable ); +} + +/* + ============= +///ScriptDocBegin +"Name: ai_3d_sighting_model( )" +"Summary: Notify an AI recipient of the visual object location of entity" +"Module: Utility" +"CallOn: Bot client or agent player" +"MandatoryArg: Entity the cue refers to." +"Example: aiEnt ai_3d_sighting_model( enemy );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +ai_3d_sighting_model( associatedEnt ) +{ + assert( isAI( self ) ); + + if ( IsDefined( level.bot_funcs ) && IsDefined( level.bot_funcs["ai_3d_sighting_model"] ) ) + return self [[ level.bot_funcs["ai_3d_sighting_model"] ]]( associatedEnt ); +} + +set_basic_animated_model( model, anime, mpanimstring) +{ + if( !isdefined ( level.anim_prop_models ) ) + level.anim_prop_models = []; + + // Would use isSP() but this runs before we can + mapname = tolower( getdvar( "mapname" ) ); + SP = true; + if ( string_starts_with( mapname, "mp_" ) ) + SP = false; + + if ( SP ) + { + level.anim_prop_models[ model ][ "basic" ] = anime; + } + else + level.anim_prop_models[ model ][ "basic" ] = mpanimstring; + +} + + /* + ============= +///ScriptDocBegin +"Name: GetClosest( , , )" +"Summary: Returns the closest entity in < array > to location < org > " +"Module: Distance" +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: friendly = GetClosest( level.player.origin, allies );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +getClosest( org, array, maxdist ) +{ + if ( !IsDefined( maxdist ) ) + maxdist = 500000; // twice the size of the grid + + ent = undefined; + foreach ( item in array ) + { + if ( !IsDefined( item ) ) + continue; + newdist = Distance( item.origin, org ); + if ( newdist >= maxdist ) + continue; + maxdist = newdist; + ent = item; + } + return ent; +} + + /* + ============= +///ScriptDocBegin +"Name: GetFarthest( , , )" +"Summary: Returns the farthest entity in < array > to location < org > " +"Module: Distance" +"MandatoryArg: : Origin to be farthest from." +"MandatoryArg: : Array of entities to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: target = GetFarthest( level.player.origin, targets );" +"SPMP: both" +///ScriptDocEnd + ============= + */ +getFarthest( org, array, maxdist ) +{ + if ( !IsDefined( maxdist ) ) + maxdist = 500000; // twice the size of the grid + + dist = 0; + ent = undefined; + foreach ( item in array ) + { + newdist = Distance( item.origin, org ); + if ( newdist <= dist || newdist >= maxdist ) + continue; + dist = newdist; + ent = item; + } + return ent; +} + +missile_setTargetAndFlightMode( target, mode, offset ) +{ + Assert( IsDefined( target ) ); + Assert( IsDefined( mode ) ); + + offset = ter_op( IsDefined( offset ), offset, (0,0,0) ); + + self Missile_SetTargetEnt( target, offset ); + + switch ( mode ) + { + case "direct": + self Missile_SetFlightmodeDirect(); + break; + case "top": + self Missile_SetFlightModeTop(); + break; + } +} + +EvFromLuminanceNits( nits ) +{ + // Equivalent to same named function in the Runtime + return log( nits + 0.000061 ) / log( 2.0 ) + 2.84; +} + + +LinearToGamma_Srgb( f ) +{ + // Equivalent to same named function in the Runtime + if ( f <= 0.0031308 ) + return f * 12.92; + else + return pow( f, 1.0 / 2.4 ) * 1.055 - 0.055; +} + + +ConvertLegacyFog( ent ) +{ + if ( !IsDefined( ent.HDRColorIntensity ) ) + { + if ( IsUsingHDR() ) + { + // Settings that don't include HDRColorIntensity are assumed to be old, legacy linear HDR fog settings in framebuffer units. + // Convert these old settings to the new style settings with gamma color and EV brightness in HDRColorIntensity. + maxRGB = max( ent.red, max( ent.green, ent.blue ) ); + ev = EvFromLuminanceNits( maxRGB * GetRadiometricUnit() ); + if ( maxRGB > 0 ) + { + ent.red /= maxRGB; + ent.green /= maxRGB; + ent.blue /= maxRGB; + ent.red = LinearToGamma_Srgb( ent.red ); + ent.green = LinearToGamma_Srgb( ent.green ); + ent.blue = LinearToGamma_Srgb( ent.blue ); + ent.HDRColorIntensity = ev; + } + else + { + ent.HDRColorIntensity = 0.0; + } + } + else + { + ent.HDRColorIntensity = 1.0; + } + } + if ( IsDefined( ent.sunFogEnabled) && ent.sunFogEnabled ) + { + if ( !IsDefined( ent.HDRSunColorIntensity ) ) + { + if ( IsUsingHDR() ) + { + // Settings that don't include HDRSunColorIntensity are assumed to be old, legacy linear HDR fog settings in framebuffer units. + // Convert these old settings to the new style settings with gamma color and EV brightness in HDRColorIntensity. + maxRGB = max( ent.red, ent.green, ent.blue ); + ev = EvFromLuminanceNits( maxRGB * GetRadiometricUnit() ); + if ( maxRGB > 0 ) + { + ent.sunRed /= maxRGB; + ent.sunGreen /= maxRGB; + ent.sunBlue /= maxRGB; + ent.sunRed = LinearToGamma_Srgb( ent.red ); + ent.sunGreen = LinearToGamma_Srgb( ent.green ); + ent.sunBlue = LinearToGamma_Srgb( ent.blue ); + ent.HDRSunColorIntensity = ev; + } + else + { + ent.HDRSunColorIntensity = 0.0; + } + } + else + { + ent.HDRSunColorIntensity = 1.0; + } + } + } +} + + +ConvertFogTech( ent ) +{ + if ( IsDefined( level.exclusive_fog_tech ) ) + { + switch( level.exclusive_fog_tech ) + { + case "dfog": + if ( level.exclusive_fog_tech == "dfog" && ent.sunFogEnabled == false ) + { + ent.sunFogEnabled = true; + ent.sunRed = 0; + ent.sunGreen = 0; + ent.sunBlue = 0; + ent.HDRSunColorIntensity = 0; + ent.sunDir = ( 0, 0, 0 ); + ent.sunBeginFadeAngle = 0; + ent.sunEndFageAngle = 0; + ent.normalFogScale = 1; + } + break; + + case "normal_fog": + if ( ent.sunFogEnabled == true ) + { + ent.sunFogEnabled = false; + } + break; + + default: + AssertMsg( "unhandled level.exclusive_fog_tech: " + level.exclusive_fog_tech ); + } + } +} + + +set_fog_to_ent_values_dfog( ent, transition_time ) +{ + if ( IsDefined( ent.sunFogEnabled ) && ent.sunFogEnabled ) + { + if ( !isPlayer( self ) ) + { + SetExpFogExt( + ent.startDist, + ent.halfwayDist, + ent.red, + ent.green, + ent.blue, + ent.HDRColorIntensity, + ent.maxOpacity, + transition_time, + ent.sunRed, + ent.sunGreen, + ent.sunBlue, + ent.HDRSunColorIntensity, + ent.sunDir, + ent.sunBeginFadeAngle, + ent.sunEndFadeAngle, + ent.normalFogScale, + ent.skyFogIntensity, + ent.skyFogMinAngle, + ent.skyFogMaxAngle, + ent.heightFogEnabled, + ent.heightFogBaseHeight, + ent.heightFogHalfPlaneDistance ); + } + else + { + self PlayerSetExpFogExt( + ent.startDist, + ent.halfwayDist, + ent.red, + ent.green, + ent.blue, + ent.HDRColorIntensity, + ent.maxOpacity, + transition_time, + ent.sunRed, + ent.sunGreen, + ent.sunBlue, + ent.HDRSunColorIntensity, + ent.sunDir, + ent.sunBeginFadeAngle, + ent.sunEndFadeAngle, + ent.normalFogScale, + ent.skyFogIntensity, + ent.skyFogMinAngle, + ent.skyFogMaxAngle, + ent.heightFogEnabled, + ent.heightFogBaseHeight, + ent.heightFogHalfPlaneDistance ); + } + } + else + { + if ( !isPlayer( self ) ) + { + SetExpFogExt( + ent.startDist, + ent.halfwayDist, + ent.red, + ent.green, + ent.blue, + ent.HDRColorIntensity, + ent.maxOpacity, + transition_time, + ent.skyFogIntensity, + ent.skyFogMinAngle, + ent.skyFogMaxAngle, + ent.heightFogEnabled, + ent.heightFogBaseHeight, + ent.heightFogHalfPlaneDistance ); + } + else + { + self PlayerSetExpFogExt( + ent.startDist, + ent.halfwayDist, + ent.red, + ent.green, + ent.blue, + ent.HDRColorIntensity, + ent.maxOpacity, + transition_time, + ent.skyFogIntensity, + ent.skyFogMinAngle, + ent.skyFogMaxAngle, + ent.heightFogEnabled, + ent.heightFogBaseHeight, + ent.heightFogHalfPlaneDistance ); + } + } +} + + +set_fog_to_ent_values( ent, transition_time ) +{ + if (!isdefined(transition_time)) + transition_time = 0; + + if ( !IsDefined( ent.skyFogIntensity ) ) + { + ent.skyFogIntensity = 0; + ent.skyFogMinAngle = 0; + ent.skyFogMaxAngle = 0; + } + + if ( !IsDefined( ent.heightFogEnabled ) ) + { + ent.heightFogEnabled = 0; + ent.heightFogBaseHeight = 0; + ent.heightFogHalfPlaneDistance = 1000; + } + + ConvertLegacyFog( ent ); + ConvertFogTech( ent ); + + if ( IsDefined( ent.atmosFogEnabled ) ) + { + AssertEx( IsDefined( ent.atmosFogSunFogColor ) ); + AssertEx( IsDefined( ent.atmosFogHazeColor ) ); + AssertEx( IsDefined( ent.atmosFogHazeStrength ) ); + AssertEx( IsDefined( ent.atmosFogHazeSpread ) ); + AssertEx( IsDefined( ent.atmosFogExtinctionStrength ) ); + AssertEx( IsDefined( ent.atmosFogInScatterStrength ) ); + AssertEx( IsDefined( ent.atmosFogHalfPlaneDistance ) ); + AssertEx( IsDefined( ent.atmosFogStartDistance ) ); + AssertEx( IsDefined( ent.atmosFogDistanceScale ) ); + AssertEx( IsDefined( ent.atmosFogSkyDistance ) ); + AssertEx( IsDefined( ent.atmosFogSkyAngularFalloffEnabled ) ); + AssertEx( IsDefined( ent.atmosFogSkyFalloffStartAngle ) ); + AssertEx( IsDefined( ent.atmosFogSkyFalloffAngleRange ) ); + AssertEx( IsDefined( ent.atmosFogSunDirection ) ); + AssertEx( IsDefined( ent.atmosFogHeightFogEnabled ) ); + AssertEx( IsDefined( ent.atmosFogHeightFogBaseHeight ) ); + AssertEx( IsDefined( ent.atmosFogHeightFogHalfPlaneDistance ) ); + + if ( level.nextgen && ent.atmosFogEnabled ) + { + // [nextgen-begin] + /# + if ( IsDefined( ent.sunFogEnabled ) && ent.sunFogEnabled ) + { + SetExpFogExtDvarsOnly( ent.startDist, ent.halfwayDist, ent.red, ent.green, ent.blue, ent.HDRColorIntensity, ent.maxOpacity, ent.sunRed, ent.sunGreen, ent.sunBlue, ent.HDRSunColorIntensity, ent.sunDir, ent.sunBeginFadeAngle, ent.sunEndFadeAngle, ent.normalFogScale, ent.skyFogIntensity, ent.skyFogMinAngle, ent.skyFogMaxAngle, ent.heightFogEnabled, ent.heightFogBaseHeight, ent.heightFogHalfPlaneDistance ); + } + else + { + SetExpFogExtDvarsOnly( ent.startDist, ent.halfwayDist, ent.red, ent.green, ent.blue, ent.HDRColorIntensity, ent.maxOpacity, ent.skyFogIntensity, ent.skyFogMinAngle, ent.skyFogMaxAngle, ent.heightFogEnabled, ent.heightFogBaseHeight, ent.heightFogHalfPlaneDistance ); + } + #/ + + if ( isPlayer( self ) ) + self PlayerSetAtmosFog( transition_time, ent.atmosFogSunFogColor, ent.atmosFogHazeColor, ent.atmosFogHazeStrength, ent.atmosFogHazeSpread, ent.atmosFogExtinctionStrength, ent.atmosFogInScatterStrength, ent.atmosFogHalfPlaneDistance, ent.atmosFogStartDistance, ent.atmosFogDistanceScale, int( ent.atmosFogSkyDistance ), ent.atmosFogSkyAngularFalloffEnabled, ent.atmosFogSkyFalloffStartAngle, ent.atmosFogSkyFalloffAngleRange, ent.atmosFogSunDirection, ent.atmosFogHeightFogEnabled, ent.atmosFogHeightFogBaseHeight, ent.atmosFogHeightFogHalfPlaneDistance ); + else + SetAtmosFog( transition_time, ent.atmosFogSunFogColor, ent.atmosFogHazeColor, ent.atmosFogHazeStrength, ent.atmosFogHazeSpread, ent.atmosFogExtinctionStrength, ent.atmosFogInScatterStrength, ent.atmosFogHalfPlaneDistance, ent.atmosFogStartDistance, ent.atmosFogDistanceScale, int( ent.atmosFogSkyDistance ), ent.atmosFogSkyAngularFalloffEnabled, ent.atmosFogSkyFalloffStartAngle, ent.atmosFogSkyFalloffAngleRange, ent.atmosFogSunDirection, ent.atmosFogHeightFogEnabled, ent.atmosFogHeightFogBaseHeight, ent.atmosFogHeightFogHalfPlaneDistance ); + // [nextgen-end] + } + else + { + /# + SetAtmosFogDvarsOnly( ent.atmosFogEnabled, ent.atmosFogSunFogColor, ent.atmosFogHazeColor, ent.atmosFogHazeStrength, ent.atmosFogHazeSpread, ent.atmosFogExtinctionStrength, ent.atmosFogInScatterStrength, ent.atmosFogHalfPlaneDistance, ent.atmosFogStartDistance, ent.atmosFogDistanceScale, int( ent.atmosFogSkyDistance ), ent.atmosFogSkyAngularFalloffEnabled, ent.atmosFogSkyFalloffStartAngle, ent.atmosFogSkyFalloffAngleRange, ent.atmosFogSunDirection, ent.atmosFogHeightFogEnabled, ent.atmosFogHeightFogBaseHeight, ent.atmosFogHeightFogHalfPlaneDistance ); + #/ + + set_fog_to_ent_values_dfog( ent, transition_time ); + } + } + else + { + set_fog_to_ent_values_dfog( ent, transition_time ); + } +} + +/* +============= +///ScriptDocBegin +"Name: add_fx( , )" +"Summary: add an effect to the level._effect array. This is used by exploder and createfx and anything that uses getfx." +"Module: Utility" +"CallOn: An entity" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: " +"SPMP: both" +///ScriptDocEnd +============= +*/ + +add_fx( fx_id, fx_path ) +{ + if ( !IsDefined( level._effect ) ) + level._effect = []; + Assert( IsDefined( fx_path ) ); + Assert( IsDefined( fx_id ) ); + level._effect[ fx_id ] = LoadFX( fx_path ); +} + +/* +============= +///ScriptDocBegin +"Name: array_sort_by_handler( , )" +"Summary: Returns the sorted version of the passed array according to the passed function handler. Exchange sort is used to order the array. Items in the array are compared using the passed compare_func" +"Module: Array" +"CallOn: " +"MandatoryArg: : Array to be sorted" +"MandatoryArg: : Function that returns a value that useable with the comparison operator, specifically the less than operator: <" +"Example: vehicle_nodes = array_sort_by_handler( vehicle_nodes, ::distance_from_player )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +array_sort_by_handler( array, compare_func ) +{ + AssertEx( IsDefined( array ), "Array not defined." ); + AssertEx( IsDefined( compare_func ), "Compare function not defined." ); + + for ( i = 0; i < array.size - 1; i++ ) + { + for ( j = i + 1; j < array.size; j++ ) + { + if ( array[ j ] [[ compare_func ]]() < array[ i ] [[ compare_func ]]() ) + { + ref = array[ j ]; + array[ j ] = array[ i ]; + array[ i ] = ref; + } + } + } + + return array; +} + +/* +============= +///ScriptDocBegin +"Name: array_sort_with_func( , , )" +"Summary: Returns the sorted version of the passed array according to the passed function handler. Items in the array are compared using the passed compare_func." +"Module: Array" +"CallOn: " +"MandatoryArg: : Array to be sorted" +"MandatoryArg: : User defined function that returns the comparison value." +"OptionalArg: If defined, the number of elements in the array that will be sorted per frame (useful for large arrays)" +"Example: vehicle_nodes = array_sort_with_func( vehicle_nodes, ::distance_from_player )" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +array_sort_with_func( array, compare_func, sort_per_frame ) +{ + AssertEx( IsDefined( array ), "Array not defined." ); + AssertEx( IsDefined( compare_func ), "Compare function not defined." ); + + if ( !IsDefined(sort_per_frame) ) + sort_per_frame = -1; + + /* + for ( i = 0; i < array.size - 1; i++ ) + { + for ( j = i + 1; j < array.size; j++ ) + { + if ( [[ compare_func ]]( array[ j ], array[ i ] ) ) + { + ref = array[ j ]; + array[ j ] = array[ i ]; + array[ i ] = ref; + } + } + } + */ + // switch from bubble sort to insertion sort, which is a bit more efficient + // both cases behave best when array.size is small and mostly sorted + prof_begin( "Array_sort" ); + + for (j = 1; j < array.size; j++) + { + key = array[j]; + for (i = j - 1; (i >= 0) && ![[ compare_func ]](array[i], key); i--) + { + array[i+1] = array[i]; + } + array[i+1] = key; + + if ( sort_per_frame > 0 && (j % sort_per_frame) == 0 ) + wait(0.05); + } + + prof_end( "Array_sort" ); + + return array; +} + + +/* +============= +///ScriptDocBegin +"Name: hide_notsolid( )" +"Summary: Hide an entity and make it truly notsolid via setContents" +"Module: Utility" +"CallOn: An entity" +"Example: model hide_notsolid();" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +hide_notsolid() +{ + if ( !isdefined( self.oldContents ) ) + { + self.oldContents = self SetContents( 0 ); + } + + self Hide(); +} + +/* +============= +///ScriptDocBegin +"Name: show_solid( )" +"Summary: Show an entity and retrieve its prior contents." +"Module: Utility" +"CallOn: An entity" +"Example: model show_solid();" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +show_solid() +{ + if ( !isai( self ) ) + self Solid(); + + if ( IsDefined( self.oldContents ) ) + { + self SetContents( self.oldContents ); + } + + self Show(); +} + + + +/* +============= +///ScriptDocBegin +"Name: SetLightingState( lightingState )" +"Summary: Specify current lighting state. Objects belong to a different lighting state will become inactivated" +"Module: Utility" +"CallOn: " +"MandatoryArg: lightingState: Specify current lighting state" +"Example: SetLightingState( 1 )" +"SPMP: both" +///ScriptDocEnd +============= +*/ + +SetLightingState( lightingState ) +{ + allEnts = GetEntArray(); + + SetOmnvar( "lighting_state", lightingState ); + + if ( !getdvarint( "r_reflectionProbeGenerate" ) ) + { + foreach ( ent in allEnts ) + { + if ( isdefined( ent.lightingstate ) && ( ent.className == "script_brushmodel" || ent.className == "script_model" ) ) + { + if ( ent.lightingstate == 0 ) + { + // do nothing + } + else if ( ent.lightingstate == lightingState ) + { + // activate it + ent show_solid(); + } + else + { + // deactivate it + ent hide_notsolid(); + } + } + } + } +} diff --git a/raw/destructible_scripts/berlin_hotel_lights_wall2.gsc b/raw/destructible_scripts/berlin_hotel_lights_wall2.gsc new file mode 100644 index 0000000..d4e8421 --- /dev/null +++ b/raw/destructible_scripts/berlin_hotel_lights_wall2.gsc @@ -0,0 +1,19 @@ +#include common_scripts\_destructible; +#using_animtree( "destructibles" ); + +main() +{ + dest_onestate("berlin_hotel_lights_wall2","berlin_hotel_lights_wall2_destroyed","fx/misc/light_blowout_wall_runner"); +} + + +dest_onestate(destructibleType,dest,fx,sound) +{ + destructible_create(destructibleType,"tag_origin",150,undefined,32); + destructible_fx( "tag_fx", fx); + destructible_state( "tag_origin", dest, undefined, undefined, "no_meele" ); + if (isdefined( sound)) + destructible_sound( sound ); +} + + diff --git a/raw/destructible_scripts/container_plastic_72x56x48_01_destp.gsc b/raw/destructible_scripts/container_plastic_72x56x48_01_destp.gsc new file mode 100644 index 0000000..07723dc --- /dev/null +++ b/raw/destructible_scripts/container_plastic_72x56x48_01_destp.gsc @@ -0,0 +1,24 @@ +#include common_scripts\_destructible; +#using_animtree( "destructibles" ); + +main() +{ + //--------------------------------------------------------------------- + // container_plastic_72x56x48_01 + //--------------------------------------------------------------------- + destructible_create( "container_plastic_72x56x48_01_destp", "tag_origin", 500, undefined, 32, "no_melee" ); + destructible_fx( "tag_origin", "vfx/destructible/container_plastic_72x56x48_01", true ); + destructible_explode( 1500, 3000, 100, 100, 10, 20, undefined, undefined, undefined, undefined, undefined, undefined, 2000, 3000 ); // force_min, force_max, rangeSP, rangeMP, mindamage, maxdamage, continueDamage, originOffset, earthQuakeScale, earthQuakeRadius, originOffset3d, delaytime, angularImpulse_min, angularImpulse_max + + destructible_state( "tag_origin", "container_plastic_72x56x48_01_dstry_destp", undefined, undefined, undefined, undefined, undefined, false ); + + destructible_part( "tag_part_01", "container_plastic_72x56x48_01_part_01_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_02", "container_plastic_72x56x48_01_part_02_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_03", "container_plastic_72x56x48_01_part_03_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_04", "container_plastic_72x56x48_01_part_04_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_05", "container_plastic_72x56x48_01_part_05_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_06", "container_plastic_72x56x48_01_part_06_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_07", "container_plastic_72x56x48_01_part_07_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + destructible_part( "tag_part_08", "container_plastic_72x56x48_01_part_08_destp", undefined, undefined, undefined, undefined, 1.0, 1.0 ); + +} \ No newline at end of file diff --git a/raw/destructible_scripts/toy_chicken.gsc b/raw/destructible_scripts/toy_chicken.gsc new file mode 100644 index 0000000..08bf9dd --- /dev/null +++ b/raw/destructible_scripts/toy_chicken.gsc @@ -0,0 +1,8 @@ +#include common_scripts\_destructible; +#include destructible_scripts\toy_chicken_common; +#using_animtree( "destructibles" ); + +main() +{ + toy_chicken_common( "" ); +} diff --git a/raw/destructible_scripts/toy_electricbox4.gsc b/raw/destructible_scripts/toy_electricbox4.gsc new file mode 100644 index 0000000..6cd585c --- /dev/null +++ b/raw/destructible_scripts/toy_electricbox4.gsc @@ -0,0 +1,17 @@ +#include common_scripts\_destructible; +#using_animtree( "destructibles" ); + +main() +{ + //--------------------------------------------------------------------- + // electric box medium toy + //--------------------------------------------------------------------- + destructible_create( "toy_electricbox4", "tag_origin", 150, undefined, 32, "no_melee" ); + destructible_splash_damage_scaler( 15 ); + destructible_fx( "tag_fx", "fx/props/electricbox4_explode", undefined, undefined, undefined, 1 ); + destructible_sound( "exp_fusebox_sparks" ); + destructible_explode( 20, 2000, 32, 32, 32, 48, undefined, 0 ); // force_min, force_max, rangeSP, rangeMP, mindamage, maxdamage, continue damage, originoffset + destructible_state( undefined, "me_electricbox4_dest", undefined, undefined, "no_melee" ); + // door + destructible_part( "tag_fx", "me_electricbox4_door", undefined, undefined, undefined, undefined, 1.0, 1.0 ); +} diff --git a/raw/destructible_scripts/toy_locker_double.gsc b/raw/destructible_scripts/toy_locker_double.gsc new file mode 100644 index 0000000..84c2282 --- /dev/null +++ b/raw/destructible_scripts/toy_locker_double.gsc @@ -0,0 +1,44 @@ +#include common_scripts\_destructible; +#using_animtree( "destructibles" ); + +main() +{ + //--------------------------------------------------------------------- + // Locker Double + //--------------------------------------------------------------------- + destructible_create( "toy_locker_double", "tag_origin", 150, undefined, 32, "no_melee" ); + + destructible_anim( %locker_broken_both_doors_1, #animtree, "setanimknob", undefined, 0, "locker_broken_both_doors_1" ); + destructible_fx( "tag_fx", "fx/props/locker_double_des_02_right", undefined, undefined, 0 ); + destructible_sound( "lockers_fast", undefined, 0 ); + + destructible_anim( %locker_broken_both_doors_2, #animtree, "setanimknob", undefined, 1, "locker_broken_both_doors_2" ); + destructible_fx( "tag_fx", "fx/props/locker_double_des_01_left", undefined, undefined, 1 ); + destructible_sound( "lockers_fast", undefined, 1 ); + + destructible_anim( %locker_broken_both_doors_4, #animtree, "setanimknob", undefined, 2, "locker_broken_both_doors_4" ); + destructible_fx( "tag_fx", "fx/props/locker_double_des_03_both", undefined, undefined, 2 ); + destructible_sound( "lockers_double", undefined, 2 ); + + destructible_anim( %locker_broken_door1_fast, #animtree, "setanimknob", undefined, 3, "locker_broken_door1_fast" ); + destructible_fx( "tag_fx", "fx/props/locker_double_des_01_left", undefined, undefined, 3 ); + destructible_sound( "lockers_fast", undefined, 3 ); + + destructible_anim( %locker_broken_door2_fast, #animtree, "setanimknob", undefined, 4, "locker_broken_door2_fast" ); + destructible_fx( "tag_fx", "fx/props/locker_double_des_02_right", undefined, undefined, 4 ); + destructible_sound( "lockers_fast", undefined, 4 ); + + destructible_anim( %locker_broken_both_doors_3, #animtree, "setanimknob", undefined, 5, "locker_broken_both_doors_3" ); + destructible_fx( "tag_fx", "fx/misc/no_effect", undefined, undefined, 5 ); + destructible_sound( "lockers_minor", undefined, 5 ); + + destructible_anim( %locker_broken_door1_slow, #animtree, "setanimknob", undefined, 6, "locker_broken_door1_slow" ); + destructible_fx( "tag_fx", "fx/misc/no_effect", undefined, undefined, 6 ); + destructible_sound( "lockers_minor", undefined, 6 ); + + destructible_anim( %locker_broken_door2_slow, #animtree, "setanimknob", undefined, 7, "locker_broken_door2_slow" ); + destructible_fx( "tag_fx", "fx/misc/no_effect", undefined, undefined, 7 ); + destructible_sound( "lockers_minor", undefined, 7 ); + + destructible_state( undefined, "com_locker_double_destroyed", undefined, undefined, "no_melee" ); +} diff --git a/raw/dev_code_post_gfx_mp b/raw/dev_code_post_gfx_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_code_pre_gfx_mp b/raw/dev_code_pre_gfx_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_common_bots_mp b/raw/dev_common_bots_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_common_core_mp b/raw/dev_common_core_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_common_mp b/raw/dev_common_mp new file mode 100644 index 0000000..0b84dec --- /dev/null +++ b/raw/dev_common_mp @@ -0,0 +1,3 @@ +^1ERROR: alias 'exo_shield_activate_npc' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_npc_lyr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_npc_lyr_02' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_plr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_plr_lyr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_plr_lyr_02' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_close' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_deactivate_lyr_plr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_deactivate_npc' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_deactivate_npc_lyr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_deactivate_plr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_open' already added but differs - undefined behavior will result.^1ERROR: alias 'stim_activate' already added but differs - undefined behavior will result.^1ERROR: alias 'stim_activate_lyr' already added but differs - undefined behavior will result.^1ERROR: alias 'stim_activate_lyr_02' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_npc' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_npc_lyr' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_npc_lyr_02' already added but differs - undefined behavior will result.^1ERROR: alias 'exo_shield_activate_plr' already added but differs - undefined behavior will result.ERROR: *** Truncating Too Many Errors *** +ERROR: *** Truncating Too Many Errors *** +ERROR: *** Truncating Too Many Errors *** diff --git a/raw/dev_development_mp b/raw/dev_development_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_comeback b/raw/dev_mp_comeback new file mode 100644 index 0000000..7bfe807 --- /dev/null +++ b/raw/dev_mp_comeback @@ -0,0 +1 @@ +^1ERROR: Too many collision tris for 'cb_power_core_lod0101' (36704 > 32768) \ No newline at end of file diff --git a/raw/dev_mp_dam b/raw/dev_mp_dam new file mode 100644 index 0000000..b144fc9 --- /dev/null +++ b/raw/dev_mp_dam @@ -0,0 +1 @@ +^1ERROR: Could not open 'mp/constbaselines/bl_ps4_mp_dam_ball.bin'^1ERROR: failed loading 'mp/constbaselines/bl_ps4_mp_dam_ball.bin' of type 'rawfile'^1ERROR: Could not open 'mp/constbaselines/bl_ps4_mp_dam_sr.bin'^1ERROR: failed loading 'mp/constbaselines/bl_ps4_mp_dam_sr.bin' of type 'rawfile'^1ERROR: Could not open 'mp/constbaselines/bl_ps4_mp_dam_twar.bin'^1ERROR: failed loading 'mp/constbaselines/bl_ps4_mp_dam_twar.bin' of type 'rawfile' \ No newline at end of file diff --git a/raw/dev_mp_detroit b/raw/dev_mp_detroit new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_greenband b/raw/dev_mp_greenband new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_instinct b/raw/dev_mp_instinct new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_lab2 b/raw/dev_mp_lab2 new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_laser2 b/raw/dev_mp_laser2 new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_levity b/raw/dev_mp_levity new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_prison b/raw/dev_mp_prison new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_prison_z b/raw/dev_mp_prison_z new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_recovery b/raw/dev_mp_recovery new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_refraction b/raw/dev_mp_refraction new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_solar b/raw/dev_mp_solar new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_terrace b/raw/dev_mp_terrace new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_mp_venus b/raw/dev_mp_venus new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_code_post_gfx_mp b/raw/dev_patch_code_post_gfx_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_code_pre_gfx_mp b/raw/dev_patch_code_pre_gfx_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_common_bots_mp b/raw/dev_patch_common_bots_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_common_core_mp b/raw/dev_patch_common_core_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_common_mp b/raw/dev_patch_common_mp new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_comeback b/raw/dev_patch_mp_comeback new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_dam b/raw/dev_patch_mp_dam new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_detroit b/raw/dev_patch_mp_detroit new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_greenband b/raw/dev_patch_mp_greenband new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_instinct b/raw/dev_patch_mp_instinct new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_lab2 b/raw/dev_patch_mp_lab2 new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_laser2 b/raw/dev_patch_mp_laser2 new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_levity b/raw/dev_patch_mp_levity new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_prison b/raw/dev_patch_mp_prison new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_recovery b/raw/dev_patch_mp_recovery new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_refraction b/raw/dev_patch_mp_refraction new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_solar b/raw/dev_patch_mp_solar new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_terrace b/raw/dev_patch_mp_terrace new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_venus b/raw/dev_patch_mp_venus new file mode 100644 index 0000000..e69de29 diff --git a/raw/dev_patch_mp_vlobby_room b/raw/dev_patch_mp_vlobby_room new file mode 100644 index 0000000..78a09ed --- /dev/null +++ b/raw/dev_patch_mp_vlobby_room @@ -0,0 +1 @@ +^1ERROR: Could not open '../dedicated/ps4/zone_source/assetlist/mp_vlobby_room.csv'^1ERROR: Could not open '../dedicated/ps4/zone_source/assetinfo/mp_vlobby_room.csv' \ No newline at end of file diff --git a/raw/maps/_utility.gsc b/raw/maps/_utility.gsc new file mode 100644 index 0000000..1bf0eaf --- /dev/null +++ b/raw/maps/_utility.gsc @@ -0,0 +1,22030 @@ +#include common_scripts\utility; +#include animscripts\utility; +#include maps\_utility_code; + +/* +============= +///ScriptDocBegin +"Name: set_HudOutline()" +"Summary: Creates a colored outline around an ent." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : String specifying the type of outline. Use 'friendly', 'enemy', 'objective', or 'neutral' " +"MandatoryArg: : Bool specifying whether or not the outline should enable depth testing" +"Example: enemy set_HudOutline( "enemy", false );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +set_HudOutline( sType, depth_enable ) +{ + num = undefined; + + AssertEx( isdefined( sType) && IsString( sType ), "set_HudOutline() needs a string! Use 'friendly', 'enemy', 'objective', or 'neutral' " ); + AssertEx( isdefined( depth_enable ), "set_HudOutline() needs to specify whether or not depth testing should be enabled" ); + + sType = ToLower( sType ); + array[ "friendly" ] = 3; + array[ "enemy" ] = 4; + array[ "objective" ] = 5; + array[ "neutral" ] = 0; + + AssertEx( IsDefineD( array[ sType ] ), "set_hudOutline() called with invalid type. Use 'friendly', 'enemy', 'objective', or 'neutral' " ); + + num = array[ sType ]; + + //0 white 1 red 2 green 3 cyan 4 orange 5 yellow 6 blue + self HudOutlineEnable( num, depth_enable ); +} + +/* +============= +///ScriptDocBegin +"Name: is_coop()" +"Summary: returns true if co-op is in use" +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_coop() +{ + if ( IsSplitScreen() || ( GetDvar( "coop" ) == "1" ) ) + { + AssertEx( IsDefined( level.player2 ), "In co-op mode but level.player2 is undefined. IsSplitScreen=" + IsSplitScreen() + " coop dvar=" + GetDvar( "coop" ) ); + return true; + } + + return false; +} + +/* +============= +///ScriptDocBegin +"Name: is_coop_online()" +"Summary: Returns true if co-op is in use and the game is online." +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_coop_online() +{ + if ( issplitscreen() ) + return false; + + if ( !is_coop() ) + return false; + + return true; +} + +/* +============= +///ScriptDocBegin +"Name: is_player_down( )" +"Summary: Returns true if a player is in last stand mode or dead." +"Module: Entity" +"CallOn: A player" +"MandatoryArg: : The player you want to check." +"Example: return is_player_down( level.player );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_player_down( player ) +{ + AssertEx( IsDefined( player ) && IsPlayer( player ), "is_player_down() requires a valid player to test." ); + + if ( player ent_flag_exist( "laststand_downed" ) ) + return player ent_flag( "laststand_downed" ); + + if ( IsDefined( player.laststand ) ) + return player.laststand; + + return !IsAlive( player ); +} + +/* +============= +///ScriptDocBegin +"Name: is_player_down_and_out( )" +"Summary: Returns true if a player is in last stand mode AND been knocked into "out" mode... where they have no weapon." +"Module: Entity" +"CallOn: A player" +"MandatoryArg: : " +"Example: bool = is_player_down_and_out( leve.player );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_player_down_and_out( player ) +{ + assertex( isdefined( player ) && isplayer( player ), "is_player_down_and_out() requires a valid player to test." ); + + if ( !isdefined( player.down_part2_proc_ran ) ) + return false; + + return player.down_part2_proc_ran; +} + +/* +============= +///ScriptDocBegin +"Name: killing_will_down()" +"Summary: Returns true if damaging the player to death will put them in laststand. Doesn't account for armor." +"Module: Entity" +"CallOn: A player" +"MandatoryArg: : " +"Example: bool = killing_will_down( level.player )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +killing_will_down( player ) +{ + AssertEx( IsDefined( player ) && IsPlayer( player ), "Invalid player." ); + + if ( laststand_enabled() ) + { + AssertEx( IsDefined( level.laststand_initialized ) && IsDefined( level.laststand_kill_will_down_func ), "Laststand not initialized." ); + + if ( IsDefined( level.laststand_kill_will_down_func ) ) + { + return player [[ level.laststand_kill_will_down_func ]](); + } + } + + return false; +} + +/* +============= +///ScriptDocBegin +"Name: is_survival()" +"Summary: returns true if map is in Special Op Survival mode." +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_survival() +{ + return ( is_specialop() && ( GetDvarInt( "so_survival" ) > 0 ) ); +} + +/* +============= +///ScriptDocBegin +"Name: laststand_enabled()" +"Summary: returns true if the player will enter last stand on death." +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +laststand_enabled() +{ + return IsDefined( level.laststand_type ) && level.laststand_type > 0; +} + +/* +============= +///ScriptDocBegin +"Name: is_specialop()" +"Summary: returns true if map is in Special Op mode." +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +is_specialop() +{ + return GetDvarInt( "specialops" ) >= 1; +} + +/* +============= +///ScriptDocBegin +"Name: convert_to_time_string( , )" +"Summary: Takes a number and returns a string in Minutes:Seconds format." +"Module: Utility" +"MandatoryArg: : The number in seconds you want to convert." +"MandatoryArg: : Option to display up to the tenths place for the seconds portion." +"Example: time_string = convert_to_time_string( time_seconds );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +convert_to_time_string( timer, show_tenths ) +{ + string = ""; + if ( timer < 0 ) + string += "-"; + + // Before computing the time string round + // up the tenths place. + timer = round_float( timer, 1, false ); + + timer_clipped = timer * 100; + timer_clipped = int( timer_clipped ); + timer_clipped = abs( timer_clipped ); + + minutes = timer_clipped / 6000; + minutes = int( minutes ); + string += minutes; + + seconds = timer_clipped / 100; + seconds = int( seconds ); + seconds -= minutes * 60; + if ( seconds < 10 ) + string += ":0" + seconds; + else + string += ":" + seconds; + + if ( IsDefined( show_tenths ) && show_tenths ) + { + tenths = timer_clipped; + tenths -= minutes * 6000; + tenths -= seconds * 100; + tenths = int( tenths/10 ); + string += "." + tenths; + } + + return string; +} + + +/* +============= +///ScriptDocBegin +"Name: round_float( , , )" +"Summary: Takes a float value and rounds that value according to the specificed place and whether or not up or down is specified." +"Module: Utility" +"MandatoryArg: : The float value to round" +"MandatoryArg: : The place to round to. Valid params are: 0 for "integer", 1 for "tenths", 2 for "hundredths", 3 for "thousandths" and 4 for "ten_thousandths"." +"OptionalArg: : Boolean value representing whether to round down or up. Defaults to down when undefined." +"Example: float_rounded = round_float( 2.339, 2, false )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +round_float( value, precision, down ) +{ + AssertEx( IsDefined( value ), "value must be defined." ); + AssertEx( IsDefined( precision ), "precision must be defined." ); + AssertEx( precision == int( precision ), "precision must be an integer." ); + + precision = int( precision ); + + if ( precision < 0 || precision > 4 ) + { + AssertMsg( "Precision value must be an integer that is >= 0 and <= 4. This was passed in: " + precision ); + return value; + } + + // Start at no decimal offset + decimal_offset = 1; + for ( i = 1; i <= precision; i++ ) + { + decimal_offset *= 10; + } + + value_clipped = value * decimal_offset; + + if ( !IsDefined( down ) || down ) + { + value_clipped = floor( value_clipped ); + } + else + { + value_clipped = ceil( value_clipped ); + } + + value = value_clipped / decimal_offset; + + return value; +} + +/* +============= +///ScriptDocBegin +"Name: round_millisec_on_sec( , , )" +"Summary: Takes milliseconds, converts to seconds, rounds to the desired precision and then converts back to milliseconds." +"Module: Utility" +"MandatoryArg: : The time in milliseconds to be rounded." +"MandatoryArg: : The placeround the value according to seconds. Valid params are: 0 for "integer", 1 for "tenths", 2 for "hundredths", 3 for "thousandths" and 4 for "ten_thousandths"." +"OptionalArg: : Boolean value representing whether to round down or up. Defaults to down when undefined." +"Example: time_mill_rounded = round_millisec_on_sec( time_mil )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +round_millisec_on_sec( value, precision, down ) +{ + // Divide by 1000 instead of multiplying by + // 0.001 because the latter causes excess + // decimal places due to floating point error - JC + value_seconds = value / 1000; + + value_seconds = round_float( value_seconds, precision, down ); + + value = value_seconds * 1000; + + return int( value ); +} + +/* +============= +///ScriptDocBegin +"Name: set_vision_set( , )" +"Summary: Sets the vision set over time" +"Module: Utility" +"MandatoryArg: : Visionset file to use" +"OptionalArg: : Time to transition to the new vision set. Defaults to 1 second." +"Example: set_vision_set( "blackout_darkness", 0.5 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_vision_set( visionset, transition_time ) +{ + if ( init_vision_set( visionset ) ) + return; + + if ( !isdefined( transition_time ) ) + transition_time = 1; + //iprintlnbold( visionset ); + VisionSetNaked( visionset, transition_time ); + SetDvar( "vision_set_current", visionset ); +} + +/* +============= +///ScriptDocBegin +"Name: set_vision_set_player( , )" +"Summary: Sets the vision set over time for a specific player in coop" +"Module: Utility" +"MandatoryArg: : Visionset file to use" +"OptionalArg: : Time to transition to the new vision set. Defaults to 1 second." +"Example: level.player2 set_vision_set_player( "blackout_darkness", 0.5 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_vision_set_player( visionset, transition_time ) +{ + if ( init_vision_set( visionset ) ) + return; + + Assert( IsDefined( self ) ); + Assert( level != self ); + + if ( !isdefined( transition_time ) ) + transition_time = 1; + self VisionSetNakedForPlayer( visionset, transition_time ); +} + +/* + ============= +///ScriptDocBegin +"Name: sun_light_fade( , , )" +"Summary: Fade to a given sunlight RGB value over the specified time" +"Module: Utility" +"MandatoryArg: : Starting RGB values (use whatever the current sun is set to)" +"MandatoryArg: : Target RGB values the sun colors should change to" +"MandatoryArg: : Time in seconds for the fade to occur" +"Example: thread sun_light_fade( (.5,.8,.75), (3.5,3.5,3.5), 2 )" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +sun_light_fade( startSunColor, endSunColor, fTime ) +{ + fTime = Int( fTime * 20 ); + + // determine difference btwn starting and target sun RGBs + increment = []; + for ( i = 0; i < 3; i++ ) + increment[ i ] = ( startSunColor[ i ] - endSunColor[ i ] ) / fTime; + + // change gradually to new sun color over time + newSunColor = []; + for ( i = 0; i < fTime; i++ ) + { + wait( 0.05 ); + for ( j = 0; j < 3; j++ ) + newSunColor[ j ] = startSunColor[ j ] - ( increment[ j ] * i ); + SetSunLight( newSunColor[ 0 ], newSunColor[ 1 ], newSunColor[ 2 ] ); + } + //set sunlight to new target values to account for rounding off decimal places + SetSunLight( endSunColor[ 0 ], endSunColor[ 1 ], endSunColor[ 2 ] ); +} + +/* + ============= +///ScriptDocBegin +"Name: ent_flag_wait( )" +"Summary: Waits until the specified flag is set on self. Even handles some default flags for ai such as 'goal' and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to wait on" +"Example: enemy ent_flag_wait( "goal" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_wait( msg ) +{ + AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" ); + + while ( IsDefined( self ) && !self.ent_flag[ msg ] ) + self waittill( msg ); +} + +/* + ============= +///ScriptDocBegin +"Name: ent_flag_wait_vehicle_node( )" +"Summary: Waits until the specified flag is set on self. Even handles some default flags for ai such as 'goal' and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to wait on" +"Example: enemy ent_flag_wait( "goal" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_wait_vehicle_node( msg ) +{ + AssertEx( IsDefined( self ) , "Attempt to check a flag on node that is is not defined" ); + + while ( IsDefined( self ) && !self.ent_flag[ msg ] ) + self waittill( msg ); +} + + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_wait_either( , )" +"Summary: Waits until either of the the specified flags are set on self. Even handles some default flags for ai such as 'goal' and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of one flag to wait on" +"MandatoryArg: : name of the other flag to wait on" +"Example: enemy ent_flag_wait( "goal", "damage" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_wait_either( flag1, flag2 ) +{ + AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" ); + + while ( IsDefined( self ) ) + { + if ( ent_flag( flag1 ) ) + return; + if ( ent_flag( flag2 ) ) + return; + + self waittill_either( flag1, flag2 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_wait_or_timeout( , )" +"Summary: Waits until either the flag gets set on self or the timer elapses. Even handles some default flags for ai such as 'goal' and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : Amount of time to wait before continuing regardless of flag." +"Example: ent_flag_wait_or_timeout( "time_to_go", 3 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_wait_or_timeout( flagname, timer ) +{ + AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" ); + + start_time = GetTime(); + while ( IsDefined( self ) ) + { + if ( self.ent_flag[ flagname ] ) + break; + + if ( GetTime() >= start_time + timer * 1000 ) + break; + + self ent_wait_for_flag_or_time_elapses( flagname, timer ); + } +} + +/* +============= +///ScriptDocBegin +"Name: ent_flag_waitopen( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +ent_flag_waitopen( msg ) +{ + AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" ); + + while ( IsDefined( self ) && self.ent_flag[ msg ] ) + self waittill( msg ); +} + +ent_flag_assert( msg ) +{ + AssertEx( !self ent_flag( msg ), "Flag " + msg + " set too soon on entity" ); +} + + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_waitopen_either( , )" +"Summary: Waits until either of the the specified flags are open on self. Even handles some default flags for ai such as 'goal' and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of one flag to waitopen on" +"MandatoryArg: : name of the other flag to waitopen on" +"Example: enemy ent_flag_waitopen_either( "goal", "damage" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_waitopen_either( flag1, flag2 ) +{ + AssertEx( ( !IsSentient( self ) && IsDefined( self ) ) || IsAlive( self ), "Attempt to check a flag on entity that is not alive or removed" ); + + while ( IsDefined( self ) ) + { + if ( !ent_flag( flag1 ) ) + return; + if ( !ent_flag( flag2 ) ) + return; + + self waittill_either( flag1, flag2 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_init( )" +"Summary: Initialize a flag to be used. All flags must be initialized before using ent_flag_set or ent_flag_wait. Some flags for ai are set by default such as 'goal', 'death', and 'damage'" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to create" +"Example: enemy ent_flag_init( "hq_cleared" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_init( message ) +{ + if ( !isDefined( self.ent_flag ) ) + { + self.ent_flag = []; + self.ent_flags_lock = []; + } + + /# + if ( IsDefined( level.first_frame ) && level.first_frame == -1 ) + AssertEx( !isDefined( self.ent_flag[ message ] ), "Attempt to reinitialize existing message: " + message + " on entity." ); + #/ + + self.ent_flag[ message ] = false; +/# + self.ent_flags_lock[ message ] = false; +#/ +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_exist( )" +"Summary: checks to see if a flag exists" +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to check" +"Example: if( enemy ent_flag_exist( "hq_cleared" ) );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_exist( message ) +{ + if ( IsDefined( self.ent_flag ) && IsDefined( self.ent_flag[ message ] ) ) + return true; + return false; +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_set_delayed( , )" +"Summary: Sets the specified flag after waiting the delay time on self, all scripts using ent_flag_wait on self will now continue." +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to set" +"MandatoryArg: : time to wait before setting the flag" +"Example: entity flag_set_delayed( "hq_cleared", 2.5 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_set_delayed( message, delay ) +{ + self endon( "death" ); + + wait( delay ); + self ent_flag_set( message ); +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_set( )" +"Summary: Sets the specified flag on self, all scripts using ent_flag_wait on self will now continue." +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to set" +"Example: enemy ent_flag_set( "hq_cleared" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_set( message ) +{ +/# + AssertEx( IsDefined( self ), "Attempt to set a flag on entity that is not defined" ); + AssertEx( IsDefined( self.ent_flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message + " on entity." ); + Assert( self.ent_flag[ message ] == self.ent_flags_lock[ message ] ); + self.ent_flags_lock[ message ] = true; +#/ + self.ent_flag[ message ] = true; + self notify( message ); +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag_clear( )" +"Summary: Clears the specified flag on self." +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to clear" +"OptionalArg: : free the flag completely, use this when you want to save a variable after you're never going to use the flag again." +"Example: enemy ent_flag_clear( "hq_cleared" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag_clear( message, remove ) +{ +/# + AssertEx( IsDefined( self ), "Attempt to clear a flag on entity that is not defined" ); + AssertEx( IsDefined( self.ent_flag[ message ] ), "Attempt to set a flag before calling flag_init: " + message + " on entity." ); + Assert( self.ent_flag[ message ] == self.ent_flags_lock[ message ] ); + self.ent_flags_lock[ message ] = false; +#/ + //do this check so we don't unneccessarily send a notify + if ( self.ent_flag[ message ] ) + { + self.ent_flag[ message ] = false; + self notify( message ); + } + + if ( IsDefined( remove ) && remove ) + { + self.ent_flag[ message ] = undefined; +/# + self.ent_flags_lock[ message ] = undefined; +#/ +} +} + +/* +============= +///ScriptDocBegin +"Name: ent_flag_clear_delayed( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +ent_flag_clear_delayed( message, delay ) +{ + wait( delay ); + self ent_flag_clear( message ); +} + + /* + ============= +///ScriptDocBegin +"Name: ent_flag( )" +"Summary: Checks if the flag is set on self. Returns true or false." +"Module: Flag" +"CallOn: Any entity (script_origin, script_struct, ai, script_model, script_brushmodel, player)" +"MandatoryArg: : name of the flag to check" +"Example: enemy ent_flag( "death" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +ent_flag( message ) +{ + AssertEx( IsDefined( message ), "Tried to check flag but the flag was not defined." ); + AssertEx( IsDefined( self.ent_flag[ message ] ), "Tried to check flag " + message + " but the flag was not initialized." ); + + return self.ent_flag[ message ]; +} + /* + ============= +///ScriptDocBegin +"Name: get_closest_to_player_view( , , )" +"Summary: Returns array member closest to player's view" +"Module: Utility" +"CallOn: Variable" +"MandatoryArg: : The array of items to test against" +"OptionalArg: : Player to test against. If undefined defaults to level.player" +"OptionalArg: : Check from the player's eye point. If undefined uses the player's origin." +"OptionalArg: : min dot" +"Example: enemy get_closest_to_player_view( enemies, level.player2, true );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_to_player_view( array, player, use_eye, min_dot ) +{ + if ( !array.size ) + return; + + if ( !isdefined( player ) ) + player = level.player; + + if ( !isdefined( min_dot ) ) + min_dot = -1; + + player_origin = player.origin; + if ( IsDefined( use_eye ) && use_eye ) + player_origin = player GetEye(); + + ent = undefined; + + player_angles = player GetPlayerAngles(); + player_forward = AnglesToForward( player_angles ); + + dot = -1; + foreach ( array_item in array ) + { + angles = VectorToAngles( array_item.origin - player_origin ); + forward = AnglesToForward( angles ); + + newdot = VectorDot( player_forward, forward ); + if ( newdot < dot ) + continue; + if ( newdot < min_dot ) + continue; + dot = newdot; + ent = array_item; + } + return ent; +} + +/* + ============= +///ScriptDocBegin +"Name: get_closest_index_to_player_view( , , )" +"Summary: Returns array index of item closest to player's view" +"Module: Utility" +"CallOn: Variable" +"MandatoryArg: : The array of items to test against" +"OptionalArg: : Player to test against. If undefined defaults to level.player" +"OptionalArg: : Check from the player's eye point. If undefined uses the player's origin." +"OptionalArg: : If true, will return the index within the array rather than the actual array element." +"Example: enemy get_closest_index_to_player_view( enemies, level.player2, true );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_index_to_player_view( array, player, use_eye ) +{ + if ( !array.size ) + return; + + if ( !isdefined( player ) ) + player = level.player; + + player_origin = player.origin; + if ( IsDefined( use_eye ) && use_eye ) + player_origin = player GetEye(); + + index = undefined; + + player_angles = player GetPlayerAngles(); + player_forward = AnglesToForward( player_angles ); + + dot = -1; + for ( i = 0; i < array.size; i++ ) + { + angles = VectorToAngles( array[ i ].origin - player_origin ); + forward = AnglesToForward( angles ); + + newdot = VectorDot( player_forward, forward ); + if ( newdot < dot ) + continue; + + dot = newdot; + index = i; + } + return index; +} + +flag_trigger_init( message, trigger, continuous ) +{ + flag_init( message ); + + if ( !isDefined( continuous ) ) + continuous = false; + + Assert( IsSubStr( trigger.classname, "trigger" ) ); + trigger thread _flag_wait_trigger( message, continuous ); + + return trigger; +} + + +flag_triggers_init( message, triggers, all ) +{ + flag_init( message ); + + if ( !isDefined( all ) ) + all = false; + + for ( index = 0; index < triggers.size; index++ ) + { + Assert( IsSubStr( triggers[ index ].classname, "trigger" ) ); + triggers[ index ] thread _flag_wait_trigger( message, false ); + } + + return triggers; +} + + +/* +============= +///ScriptDocBegin +"Name: flag_set_delayed( , )" +"Summary: Sets the specified flag after waiting the delay time, all scripts using flag_wait will now continue." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of the flag to set" +"MandatoryArg: : time to wait before setting the flag" +"Example: flag_set_delayed( "hq_cleared", 2.5 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +flag_set_delayed( message, delay ) +{ + wait( delay ); + flag_set( message ); +} + +/* +============= +///ScriptDocBegin +"Name: flag_clear_delayed( , )" +"Summary: Clears the specified flag after waiting the delay time, all scripts using flag_waitopen will now continue." +"Module: Flag" +"CallOn: " +"MandatoryArg: : name of the flag to clear" +"MandatoryArg: : time to wait before clearing the flag" +"Example: flag_clear_delayed( "hq_cleared", 2.5 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +flag_clear_delayed( message, delay ) +{ + wait( delay ); + flag_clear( message ); +} + +level_end_save() +{ + if ( arcadeMode() ) + return; + + if ( level.MissionFailed ) + return; + + if ( flag( "game_saving" ) ) + return; + + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[ i ]; + if ( !isAlive( player ) ) + return; + } + + flag_set( "game_saving" ); + + imagename = "levelshots / autosave / autosave_" + level.script + "end"; + + // string not found for AUTOSAVE_AUTOSAVE + SaveGame( "levelend", &"AUTOSAVE_AUTOSAVE", imagename, true, true );// does not print "Checkpoint Reached" + + flag_clear( "game_saving" ); +} + +/* +============= +///ScriptDocBegin +"Name: add_extra_autosave_check( , , )" +"Summary: Adds an extra autosave check that must pass for an autosave to happen" +"Module: Utility" +"CallOn: An entity" +"MandatoryArg: : identify the index, of this autosave check ( so you can overwrite it )" +"MandatoryArg: : function should return true for the check to pass" +"MandatoryArg: : this is what will print to the console when this check fails" +"Example: add_extra_autosave_check( "boat_check_trailing" , ::autosave_boat_check_trailing , "trailing too far behind the enemy boat." );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +add_extra_autosave_check( name, func, msg ) +{ + level._extra_autosave_checks[ name ] = []; + level._extra_autosave_checks[ name ][ "func" ] = func; + level._extra_autosave_checks[ name ][ "msg" ] = msg; +} + +/* +============= +///ScriptDocBegin +"Name: remove_extra_autosave_check( )" +"Summary: removes the extra autosave game condtions added by add_extra_autosave_check" +"Module: Utility" +"CallOn: An entity" +"MandatoryArg: : Name" +"Example: add_extra_autosave_check( "boat_check_trailing" ); " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +remove_extra_autosave_check( name ) +{ + level._extra_autosave_checks[ name ] = undefined; +} + +/* +============= +///ScriptDocBegin +"Name: autosave_stealth()" +"Summary: autosave the game if it's 'safe' to do so in a stealth level, internally threaded and handles any 'dangerous' situations that we wouldn't want to save at during stealth gameplay" +"Module: Autosave" +"CallOn: " +"Example: autosave_stealth();" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +autosave_stealth() +{ + thread autosave_by_name_thread( "autosave_stealth", 8, true ); +} + +/* +============= +///ScriptDocBegin +"Name: autosave_stealth_silent()" +"Summary: silently autosaves the game if it's 'safe' to do so in a stealth level, internally threaded and handles any 'dangerous' situations that we wouldn't want to save at during stealth gameplay" +"Module: Autosave" +"CallOn: " +"Example: autosave_stealth_silent();" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +autosave_stealth_silent() +{ + thread autosave_by_name_thread( "autosave_stealth", 8, true, true ); +} + +/* +============= +///ScriptDocBegin +"Name: autosave_tactical()" +"Summary: autosave the game if it's 'safe' during tactical gameplay...meaning don't autosave if the player has any current enemies or has thrown a grenade. ( internally threaded )" +"Module: Autosave" +"CallOn: " +"Example: autosave_tactical();" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +autosave_tactical() +{ + autosave_tactical_setup(); + thread autosave_tactical_proc(); +} + +/* +============= +///ScriptDocBegin +"Name: autosave_by_name( )" +"Summary: autosave the game with the specified save name" +"Module: Autosave" +"CallOn: " +"MandatoryArg: : name of the save file to create" +"Example: thread autosave_by_name( "building2_cleared" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +autosave_by_name( name ) +{ + thread autosave_by_name_thread( name ); +} + +/* +============= +///ScriptDocBegin +"Name: autosave_by_name_silent( )" +"Summary: autosave the game with the specified save name without printing a checkpoint msg" +"Module: Autosave" +"CallOn: " +"MandatoryArg: : name of the save file to create" +"Example: thread autosave_by_name_silent( "building2_cleared" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= +*/ +autosave_by_name_silent( name ) +{ + thread autosave_by_name_thread( name, undefined, undefined, true ); +} + +autosave_by_name_thread( name, timeout, doStealthChecks, suppress_hint ) +{ + if ( !isDefined( level.curAutoSave ) ) + level.curAutoSave = 1; + + imageName = "levelshots/autosave/autosave_" + level.script + level.curAutoSave; + result = level maps\_autosave::tryAutoSave( level.curAutoSave, "autosave", imagename, timeout, doStealthChecks, suppress_hint ); + if ( IsDefined( result ) && result ) + level.curAutoSave++; +} + +/* +============= +///ScriptDocBegin +"Name: autosave_or_timeout( , )" +"Summary: Autosaves with the specified name but times out if the time elapses" +"Module: Autosave" +"MandatoryArg: : The name" +"MandatoryArg: : The timeout" +"Example: autosave_or_timeout( "thename", 3.5 )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +autosave_or_timeout( name, timeout ) +{ + thread autosave_by_name_thread( name, timeout ); +} + +/* +============= +///ScriptDocBegin +"Name: debug_message( , , , )" +"Summary: Prints 3d debug text at the specified location for a duration of time." +"Module: Debug" +"MandatoryArg: : String to print" +"MandatoryArg: : Location of string ( x, y, z )" +"OptionalArg: : Time to keep the string on screen. Defaults to 5 seconds." +"OptionalArg: : Overrides any origin value so that message can print on a moving entity" +"Example: debug_message( "I am the enemy", enemy.origin, 3.0 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +debug_message( message, origin, duration, entity ) +{ + if ( !isDefined( duration ) ) + duration = 5; + + if ( IsDefined( entity ) ) + { + entity endon( "death" ); + origin = entity.origin; + } + + + for ( time = 0; time < ( duration * 20 ); time++ ) + { + if ( !isdefined( entity ) ) + Print3d( ( origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 ); + else + Print3d( entity.origin, message, ( 0.48, 9.4, 0.76 ), 0.85 ); + wait 0.05; + } +} + +/* +============= +///ScriptDocBegin +"Name: debug_message_ai( , )" +"Summary: Prints 3d debug text at the ai's origin and follows the ai that passed the function." +"Module: Debug" +"CallOn: An AI" +"MandatoryArg: : String to print" +"OptionalArg: : Time to keep the string on screen. Defaults to 5 seconds." +"Example: debug_message_ai( "I am the enemy", 3.0 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +debug_message_ai( message, duration ) +{ + self notify( "debug_message_ai" ); + self endon( "debug_message_ai" ); + self endon( "death" ); + + if ( !isDefined( duration ) ) + duration = 5; + + for ( time = 0; time < ( duration * 20 ); time++ ) + { + Print3d( ( self.origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 ); + wait 0.05; + } +} + +/* +============= +///ScriptDocBegin +"Name: debug_message_clear( , , , )" +"Summary: Prints 3d debug text at the specified location for a duration of time, but can be cleared before the normal time has passed if a notify occurs." +"Module: Debug" +"MandatoryArg: : String to print" +"MandatoryArg: : Location of string ( x, y, z )" +"OptionalArg: : Time to keep the string on screen. Defaults to 5 seconds." +"OptionalArg: : Level notify string that will make this text go away before the time expires." +"Example: debug_message( "I am the enemy", enemy.origin, 3.0, "enemy died" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +debug_message_clear( message, origin, duration, extraEndon ) +{ + if ( IsDefined( extraEndon ) ) + { + level notify( message + extraEndon ); + level endon( message + extraEndon ); + } + else + { + level notify( message ); + level endon( message ); + } + + if ( !isDefined( duration ) ) + duration = 5; + + for ( time = 0; time < ( duration * 20 ); time++ ) + { + Print3d( ( origin + ( 0, 0, 45 ) ), message, ( 0.48, 9.4, 0.76 ), 0.85 ); + wait 0.05; + } +} + +precache( model ) +{ + ent = Spawn( "script_model", ( 0, 0, 0 ) ); + ent.origin = level.player GetOrigin(); + ent SetModel( model ); + ent Delete(); +} + + +closerFunc( dist1, dist2 ) +{ + return dist1 >= dist2; +} + +fartherFunc( dist1, dist2 ) +{ + return dist1 <= dist2; +} + + /* + ============= +///ScriptDocBegin +"Name: getClosestFx( , , )" +"Summary: Returns the closest fx struct created by createfx in < fxarray > to location < org > " +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of createfx structs to check distance on. These are obtained with getfxarraybyID( )" +"OptionalArg: : Minimum distance to check" +"Example: fxstruct = getClosestFx( hallway_tv, fxarray );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +getClosestFx( org, fxarray, dist ) +{ + return compareSizesFx( org, fxarray, dist, ::closerFunc ); +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_point( , , )" +"Summary: Returns the closest point from array < points > from location < origin > " +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of points to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: target = getFarthest( level.player.origin, targets );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_point( origin, points, maxDist ) +{ + Assert( points.size ); + + closestPoint = points[ 0 ]; + dist = Distance( origin, closestPoint ); + + for ( index = 0; index < points.size; index++ ) + { + testDist = Distance( origin, points[ index ] ); + if ( testDist >= dist ) + continue; + + dist = testDist; + closestPoint = points[ index ]; + } + + if ( !isDefined( maxDist ) || dist <= maxDist ) + return closestPoint; + + return undefined; +} + + /* + ============= +///ScriptDocBegin +"Name: get_farthest_ent( , )" +"Summary: Returns the farthest entity in from location < origin > " +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be farthest from." +"MandatoryArg: : Array of entities to choose from" +"Example: dude = get_farthest_ent( level.player.origin, aFriendlies );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_farthest_ent( org, array ) +{ + if ( array.size < 1 ) + return; + + dist = Distance( array[ 0 ] GetOrigin(), org ); + ent = array[ 0 ]; + for ( i = 0; i < array.size; i++ ) + { + newdist = Distance( array[ i ] GetOrigin(), org ); + if ( newdist < dist ) + continue; + dist = newdist; + ent = array[ i ]; + } + return ent; +} + + + /* + ============= +///ScriptDocBegin +"Name: get_within_range( , , )" +"Summary: Returns all elements from the array that are within DIST range to ORG." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: close_ai = get_within_range( level.player.origin, ai, 500 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_within_range( org, array, dist ) +{ + guys = []; + for ( i = 0; i < array.size; i++ ) + { + if ( Distance( array[ i ].origin, org ) <= dist ) + guys[ guys.size ] = array[ i ]; + } + return guys; +} + + /* + ============= +///ScriptDocBegin +"Name: get_outside_range( , , )" +"Summary: Returns all elements from the array that are outside DIST range to ORG." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: close_ai = get_outside_range( level.player.origin, ai, 500 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_outside_range( org, array, dist ) +{ + guys = []; + for ( i = 0; i < array.size; i++ ) + { + if ( Distance( array[ i ].origin, org ) > dist ) + guys[ guys.size ] = array[ i ]; + } + return guys; +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_living( , , )" +"Summary: Returns the closest living entity from the array from the origin" +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on" +"OptionalArg: : Maximum distance to check" +"Example: kicker = get_closest_living( node.origin, ai );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_living( org, array, dist ) +{ + if ( !isdefined( dist ) ) + dist = 9999999; + if ( array.size < 1 ) + return; + ent = undefined; + for ( i = 0; i < array.size; i++ ) + { + if ( !isalive( array[ i ] ) ) + continue; + newdist = Distance( array[ i ].origin, org ); + if ( newdist >= dist ) + continue; + dist = newdist; + ent = array[ i ]; + } + return ent; +} + +/* +============= +///ScriptDocBegin +"Name: get_highest_dot( , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_highest_dot( start, end, array ) +{ + if ( !array.size ) + return; + + ent = undefined; + + angles = VectorToAngles( end - start ); + dotforward = AnglesToForward( angles ); + dot = -1; + + foreach ( member in array ) + { + angles = VectorToAngles( member.origin - start ); + forward = AnglesToForward( angles ); + + newdot = VectorDot( dotforward, forward ); + if ( newdot < dot ) + continue; + dot = newdot; + ent = member; + } + return ent; +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_index( , , )" +"Summary: same as getClosest but returns the closest entity's array index instead of the actual entity." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Array of entities to check distance on." +"OptionalArg: : Maximum distance to check" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_index( org, array, dist ) +{ + if ( !isdefined( dist ) ) + dist = 9999999; + if ( array.size < 1 ) + return; + index = undefined; + foreach ( i, ent in array ) + { + newdist = Distance( ent.origin, org ); + if ( newdist >= dist ) + continue; + dist = newdist; + index = i; + } + return index; +} + +/* +============= +///ScriptDocBegin +"Name: get_closest_exclude( , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_closest_exclude( org, ents, excluders ) +{ + if ( !isdefined( ents ) ) + return undefined; + + range = 0; + if ( IsDefined( excluders ) && excluders.size ) + { + exclude = []; + for ( i = 0; i < ents.size; i++ ) + exclude[ i ] = false; + + for ( i = 0; i < ents.size; i++ ) + for ( p = 0; p < excluders.size; p++ ) + if ( ents[ i ] == excluders[ p ] ) + exclude[ i ] = true; + + found_unexcluded = false; + for ( i = 0; i < ents.size; i++ ) + if ( ( !exclude[ i ] ) && ( IsDefined( ents[ i ] ) ) ) + { + found_unexcluded = true; + range = Distance( org, ents[ i ].origin ); + ent = i; + i = ents.size + 1; + } + + if ( !found_unexcluded ) + return( undefined ); + } + else + { + for ( i = 0; i < ents.size; i++ ) + if ( IsDefined( ents[ i ] ) ) + { + range = Distance( org, ents[ 0 ].origin ); + ent = i; + i = ents.size + 1; + } + } + + ent = undefined; + + for ( i = 0; i < ents.size; i++ ) + if ( IsDefined( ents[ i ] ) ) + { + exclude = false; + if ( IsDefined( excluders ) ) + { + for ( p = 0; p < excluders.size; p++ ) + if ( ents[ i ] == excluders[ p ] ) + exclude = true; + } + + if ( !exclude ) + { + newrange = Distance( org, ents[ i ].origin ); + if ( newrange <= range ) + { + range = newrange; + ent = i; + } + } + } + + if ( IsDefined( ent ) ) + return ents[ ent ]; + else + return undefined; +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_player( )" +"Summary: Returns the closest coop player to the specified origin." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"Example: player = get_closest_player( tank.origin );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_player( org ) +{ + if ( level.players.size == 1 ) + return level.player; + + player = getClosest( org, level.players ); + return player; +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_player_healthy( )" +"Summary: Returns the closest player that is not bleeding out to the specified origin." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"Example: player = get_closest_player_healthy( tank.origin );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_player_healthy( org ) +{ + // JC-Laststand:ToDo - This assumes that all logic has ended once both players have gone down. This "may" be incorrect after last stand works in sp Specops + if ( level.players.size == 1 ) + return level.player; + + healthyPlayers = get_players_healthy(); + + player = getClosest( org, healthyPlayers ); + + return player; +} + + /* + ============= +///ScriptDocBegin +"Name: get_players_healthy()" +"Summary: Returns all players not bleeding out." +"Module: Utility" +"Example: players = get_players_healthy();" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_players_healthy() +{ + healthy_players = []; + foreach ( player in level.players ) + { + if ( is_player_down( player ) ) + continue; + + healthy_players[ healthy_players.size ] = player; + } + + return healthy_players; +} + + /* + ============= +///ScriptDocBegin +"Name: get_closest_ai( , )" +"Summary: Returns the closest AI of the specified team to the specified origin." +"Module: Distance" +"CallOn: " +"MandatoryArg: : Origin to be closest to." +"MandatoryArg: : Team to use. Can be "allies", "axis", or "both"." +"Example: friendly = get_closest_ai( level.player.origin, "allies" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_closest_ai( org, team, excluders ) +{ + if ( IsDefined( team ) ) + ents = GetAIArray( team ); + else + ents = GetAIArray(); + + if ( ents.size == 0 ) + return undefined; + + if ( IsDefined( excluders ) ) + { + Assert( excluders.size > 0 ); + ents = array_remove_array( ents, excluders ); + } + + return getClosest( org, ents ); +} + +/* +============= +///ScriptDocBegin +"Name: get_closest_ai_exclude( , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_closest_ai_exclude( org, team, excluders ) +{ + if ( IsDefined( team ) ) + ents = GetAIArray( team ); + else + ents = GetAIArray(); + + if ( ents.size == 0 ) + return undefined; + + return get_closest_exclude( org, ents, excluders ); +} + +/* +============= +///ScriptDocBegin +"Name: get_progress( , , , )" +"Summary: Gets the progress of the given org between the start and end points. Supply the distance for optimization." +"Module: Vector" +"CallOn: nothing" +"MandatoryArg: : start point." +"MandatoryArg: : end point." +"MandatoryArg: : the current point of the progression." +"OptionalArg: : the distance between start and end point (give for optimization, if not given, will calculate distance)." +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_progress( start, end, org, dist ) +{ + if (!IsDefined(dist)) + dist = Distance( start, end ); + + dist = max(0.01, dist); // avoid division by zero + normal = vectorNormalize( end - start ); + vec = org - start; + progress = vectorDot( vec, normal ); + progress = progress / dist; + progress = clamp(progress, 0, 1); + return progress; +} + +/* +============= +///ScriptDocBegin +"Name: can_see_origin( , )" +"Summary: Checks to see if the specified point is within the player's view." +"Module: Sentient" +"CallOn: An AI or player" +"MandatoryArg: : Origin you wish to see whether the player or AI can see." +"OptionalArg: : Set to determine whether the sight trace should check against characters." +"Example: level.player can_see_origin( entity.origin, true );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +can_see_origin( origin, test_characters ) +{ + AssertEx( IsDefined( origin ), "can_see_origin() requires a valid origin to be passed in." ); + AssertEx( IsPlayer( self ) || IsAI( self ), "can_see_origin() can only be called on a player or AI." ); + + if ( !isdefined( test_characters ) ) + test_characters = true; + + // if not in FOV, return false + if ( !self point_in_fov( origin ) ) + { + return false; + } + + // if in FOV but sight trace fails, return false + if ( !SightTracePassed( self GetEye(), origin, test_characters, self ) ) + { + return false; + } + + // if in FOV with successful trace, return true + return true; +} + +/* +============= +///ScriptDocBegin +"Name: point_in_fov( )" +"Summary: Checks to see if the point is within a standard player view FOV." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : The point to check if it is within view." +"Example: level.player point_in_fov( weapon_respawn.origin );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +point_in_fov( origin ) +{ + forward = AnglesToForward( self.angles ); + normalVec = VectorNormalize( origin - self.origin ); + + dot = VectorDot( forward, normalVec ); + return dot > 0.766;// fov = 80 +} + + /* + ============= +///ScriptDocBegin +"Name: stop_magic_bullet_shield()" +"Summary: Stops magic bullet shield on an AI, setting his health back to a normal value and making him vulnerable to death." +"Module: AI" +"CallOn: AI" +"Example: friendly stop_magic_bullet_shield();" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +stop_magic_bullet_shield() +{ + self notify( "stop_magic_bullet_shield" ); + AssertEx( IsDefined( self.magic_bullet_shield ) && self.magic_bullet_shield, "Tried to stop magic bullet shield on a guy without magic bulletshield" ); + + if ( IsAI( self ) ) + self.attackeraccuracy = 1; + + self.magic_bullet_shield = undefined; + self.damageShield = false; + + self notify( "internal_stop_magic_bullet_shield" ); + + /# + self.deletable_magic_bullet_shield = undefined; + #/ +} + +// For future projects we should distinguish between "death" and "deletion" +// Since we currently do not, bulletshield has to be turned off before deleting characters, or you will get the 2nd assert below +magic_bullet_death_detection() +{ + /# + self endon( "internal_stop_magic_bullet_shield" ); + export = self.export; + entnum = self GetEntNum(); + + self waittill( "death" ); + if ( IsDefined( self ) ) + AssertEx( 0, "Magic bullet shield guy with export(" + export + ") enuNum(" + entnum + ") died some how." ); + else + AssertEx( 0, "Magic bullet shield guy with export(" + export + ") enuNum(" + entnum + ") died, most likely deleted from spawning guys." ); + + export = export;// extra line so you can get this info in the debugger without a breakpoint. + + #/ +} + + /* + ============= +///ScriptDocBegin +"Name: magic_bullet_shield( )" +"Summary: Makes an AI invulnerable to death. When he gets shot, he is ignored by enemies for < time > seconds and his health is regenerated." +"Module: AI" +"CallOn: AI" +"OptionalArg: : Set this to make the AI not script error on death, like if you want the guy to be deletable." +"Example: friendly thread magic_bullet_shield();" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +magic_bullet_shield( no_death_detection ) +{ + AssertEx( !IsPlayer( self ), "magic_bullet_shield is only valid for AI. Use EnableInvulnerability, EnableDeathShield or EnableHealthShield for a player" ); + + if ( IsAI( self ) )// AI only + { + AssertEx( IsAlive( self ), "Tried to do magic_bullet_shield on a dead or undefined guy." ); + AssertEx( !self.delayedDeath, "Tried to do magic_bullet_shield on a guy about to die." ); + AssertEx( !isDefined( self.Melee ), "Trying to turn on magic_bullet_shield while melee in progress (might be about to die)." ); + } + else // for drones + { + self.health = 100000; + } + + + self endon( "internal_stop_magic_bullet_shield" ); + AssertEx( !isdefined( self.magic_bullet_shield ), "Can't call magic bullet shield on a character twice. Use make_hero and remove_heroes_from_array so that you don't end up with shielded guys in your logic." ); + + if ( IsAI( self ) )// AI only + self.attackeraccuracy = 0.1; + + /# + if ( !isdefined( no_death_detection ) ) + { + thread magic_bullet_death_detection(); + } + else + { + AssertEx( no_death_detection, "no_death_detection must be undefined or true" ); + self.deletable_magic_bullet_shield = true; + } + #/ + + self notify( "magic_bullet_shield" ); + self.magic_bullet_shield = true; + self.damageShield = true; + self.noragdoll = true; +} + + +/* +============= +///ScriptDocBegin +"Name: disable_long_death( )" +"Summary: Disables long death on Self" +"Module: Utility" +"CallOn: An enemy AI" +"Example: level.zakhaev disable_long_death()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +disable_long_death() +{ + AssertEx( IsAlive( self ), "Tried to disable long death on a non living thing" ); + self.a.disableLongDeath = true; +} + +/* +============= +///ScriptDocBegin +"Name: enable_long_death( )" +"Summary: Enables long death on Self" +"Module: Utility" +"CallOn: An enemy AI" +"Example: level.zakhaev enable_long_death(0" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +enable_long_death() +{ + AssertEx( IsAlive( self ), "Tried to enable long death on a non living thing" ); + self.a.disableLongDeath = false; +} + +/* +============= +///ScriptDocBegin +"Name: enable_blood_pool( )" +"Summary: enables blood pools on AI deaths( on by default )" +"Module: AI" +"CallOn: An AI" +"Example: ai enable_blood_pool()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +enable_blood_pool() +{ + self.skipBloodPool = undefined; +} + +/* +============= +///ScriptDocBegin +"Name: disable_blood_pool( )" +"Summary: disables blood pools on AI deaths( on by default )" +"Module: AI" +"CallOn: An AI" +"Example: ai disable_blood_pool()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +disable_blood_pool() +{ + self.skipBloodPool = true; +} + +/* +============= +///ScriptDocBegin +"Name: deletable_magic_bullet_shield()" +"Summary: A version of magic bullet shield that does not error if the AI dies. Useful for guys that can be deleted but you want them to have aspects of MBS." +"Module: AI" +"CallOn: AI" +"Example: friendly thread magic_bullet_shield();" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +deletable_magic_bullet_shield() +{ + magic_bullet_shield( true ); +} + + +/* +============= +///ScriptDocBegin +"Name: get_ignoreme( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_ignoreme() +{ + return self.ignoreme; +} + +/* +============= +///ScriptDocBegin +"Name: set_ignoreme( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_ignoreme( val ) +{ + AssertEx( IsSentient( self ), "Non ai tried to set ignoreme" ); + self.ignoreme = val; +} + +/* +============= +///ScriptDocBegin +"Name: set_ignoreall( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_ignoreall( val ) +{ + AssertEx( IsSentient( self ), "Non ai tried to set ignoraell" ); + self.ignoreall = val; +} + +/* +============= +///ScriptDocBegin +"Name: set_IgnoreSonicAoE( )" +"Summary: Sets if Sonic AOE can affect an AI, set to true or undefined" +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : true or undefined" +"Example: guy set_IgnoreSonicAoE( true );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_IgnoreSonicAoE( val ) +{ + AssertEx( IsSentient( self ), "Non ai tried to set IgnoreSonicAoE" ); + self.IgnoreSonicAoE = val; +} + +/* +============= +///ScriptDocBegin +"Name: set_favoriteenemy( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_favoriteenemy( enemy ) +{ + self.favoriteenemy = enemy; +} + +/* +============= +///ScriptDocBegin +"Name: get_pacifist( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_pacifist() +{ + return self.pacifist; +} + +/* +============= +///ScriptDocBegin +"Name: set_pacifist( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_pacifist( val ) +{ + AssertEx( IsSentient( self ), "Non ai tried to set pacifist" ); + self.pacifist = val; +} + +ignore_me_timer( time ) +{ + self notify( "new_ignore_me_timer" ); + self endon( "new_ignore_me_timer" ); + self endon( "death" ); + + if ( !isdefined( self.ignore_me_timer_prev_value ) ) + self.ignore_me_timer_prev_value = self.ignoreme; + + ai = GetAIArray( "bad_guys" ); + + foreach ( guy in ai ) + { + if ( !isalive( guy.enemy ) ) + continue; + if ( guy.enemy != self ) + continue; + + guy ClearEnemy(); + } + + self.ignoreme = true; + wait( time ); + + self.ignoreme = self.ignore_me_timer_prev_value; + self.ignore_me_timer_prev_value = undefined; +} + + + /* + ============= +///ScriptDocBegin +"Name: delete_exploder( )" +"Summary: deletes an exploder and its brushes forever." +"Module: Level" +"CallOn: Level" +"MandatoryArg: : number of the exploder that you want to delete" +"Example: delete_exploder( 3 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +delete_exploder( num ) +{ + common_scripts\_exploder::delete_exploder_proc( num ); +} + +/* +============= +///ScriptDocBegin +"Name: hide_exploder_models( )" +"Summary: Hides the models for an exploder." +"Module: Level" +"CallOn: Level" +"MandatoryArg: : number of the exploder that you want to hide" +"Example: hide_exploder_models( 3 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +hide_exploder_models( num ) +{ + common_scripts\_exploder::hide_exploder_models_proc( num ); + } + +/* +============= +///ScriptDocBegin +"Name: show_exploder_models( )" +"Summary: shows the models for an exploder." +"Module: Level" +"CallOn: Level" +"MandatoryArg: : number of the exploder that you want to hide" +"Example: show_exploder_models( 3 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +show_exploder_models( num ) +{ + common_scripts\_exploder::show_exploder_models_proc( num ); + } + +/* +============= +///ScriptDocBegin +"Name: stop_exploder( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +stop_exploder( num ) +{ + common_scripts\_exploder::stop_exploder_proc(num); + } + +/* +============= +///ScriptDocBegin +"Name: get_exploder_array( )" +"Summary: Return an array of exploders" +"Module: Utility" +"MandatoryArg: : Exploder num/name" +"Example: exploders = get_exploder_array( "boom" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_exploder_array( msg ) +{ + return common_scripts\_exploder::get_exploder_array_proc( msg ); + } + +flood_spawn( spawners ) +{ + maps\_spawner::flood_spawner_scripted( spawners ); +} + +/* +============= +///ScriptDocBegin +"Name: set_ambient( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : the name of the ambient track. This will set the ambient stream, dynamic ambience, mix, reverb, and occlusion." +"OptionalArg: : the crossfade time to the new ambience." +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_ambient( track, fade ) +{ + soundscripts\_audio_zone_manager::AZM_start_zone( track, fade ); +} + +/* +============= +///ScriptDocBegin +"Name: force_crawling_death( , , , )" +"Summary: Force an AI to crawl" +"Module: Utility" +"CallOn: An AI" +"MandatoryArg: : The angle to crawl" +"MandatoryArg: : How many times to crawl" +"OptionalArg: : Sets self.a.custom_crawling_death_array" +"OptionalArg: : Sets self.nofallanim" +"Example: self force_crawling_death( self.angles[ 1 ], 2, level.scr_anim[ "crawl_death_1" ], 1 );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +force_crawling_death( angle, num_crawls, array, nofallanim ) +{ + if ( !isdefined( num_crawls ) ) + num_crawls = 4; + + self thread force_crawling_death_proc( angle, num_crawls, array, nofallanim ); +} + +#using_animtree( "generic_human" ); +override_crawl_death_anims() +{ + if ( IsDefined( self.a.custom_crawling_death_array ) ) + { + self.a.array[ "crawl" ] = self.a.custom_crawling_death_array[ "crawl" ]; + self.a.array[ "death" ] = self.a.custom_crawling_death_array[ "death" ]; + self.a.crawl_fx_rate = self.a.custom_crawling_death_array[ "blood_fx_rate" ]; + if( isdefined( self.a.custom_crawling_death_array[ "blood_fx" ] ) ) + self.a.crawl_fx = self.a.custom_crawling_death_array[ "blood_fx" ]; + } + + self.a.array[ "stand_2_crawl" ] = []; + self.a.array[ "stand_2_crawl" ][ 0 ] = %dying_stand_2_crawl_v3; + + if ( IsDefined( self.nofallanim ) ) + self.a.pose = "prone"; + + self OrientMode( "face angle", self.a.force_crawl_angle ); + self.a.force_crawl_angle = undefined; +} + +force_crawling_death_proc( angle, num_crawls, array, nofallanim ) +{ + self.forceLongDeath = true; + self.a.force_num_crawls = num_crawls; + self.noragdoll = true; + self.nofallanim = nofallanim; + + self.a.custom_crawling_death_array = array; + self.crawlingPainAnimOverrideFunc = ::override_crawl_death_anims; + + self.maxhealth = 100000; + self.health = 100000; + self enable_long_death(); + + if ( !isdefined( nofallanim ) || nofallanim == false ) + self.a.force_crawl_angle = angle + 181.02; + else + { + self.a.force_crawl_angle = angle; + self thread animscripts\notetracks::noteTrackPoseCrawl(); + } +} + + +never_saw_it_coming() +{ + self endon( "death" ); + while ( 1 ) + { + highJump = self IsHighJumping(); + //Wait for a jump + if( highJump ) + { + + exododge = self waittill_any_return( "exo_dodge", "player_boost_land", "disable_high_jump" ); + if ( !IsDefined( exododge ) || ( exododge == "player_boost_land" || exododge == "disable_high_jump" ) ) + continue; + if ( !IsDefined( self.never_saw_it_coming ) ) + self.never_saw_it_coming = true; + self waittill_any( "player_boost_land", "disable_high_jump" ); + waitframe; + self.never_saw_it_coming = undefined; + } + waitframe; + } +} + +check_man_overboard( ) +{ + if ( GetDvar( "mapname", "undefined" ) != "sanfran_b" ) + return; + if ( !IsDefined( level.player.man_overboard ) || !level.player.man_overboard ) + level.player.man_overboard = true; + + wait(2.0); + + level.player.man_overboard = undefined; +} + +monitor_genius_achievement(attacker, type, weapon) +{ + + if( type != "MOD_GRENADE" ) + { + attacker.genius_achievement = undefined; + return; + } + + + if ( !isdefined( attacker.genius_achievement ) ) + attacker.genius_achievement = 1; + else + attacker.genius_achievement++; + + if ( attacker.genius_achievement == 4 ) + giveachievement_wrapper("SMART_GRENADE_KILL"); + + wait(0.1); + attacker.genius_achievement = undefined; + +} + +start_monitor_escape_artist() +{ + add_global_spawn_function( "axis", ::monitor_escape_artist ); + array_thread( GetAIArray( "axis" ), ::monitor_escape_artist ); + + level.grenade_id = 0; + level.player.escape_artist = []; +} + +monitor_escape_artist() +{ + while( 1 ) + { + grenade = undefined; + self waittill( "grenade_fire", grenade, weaponname ); + grenade.unique_id = level.grenade_id; + level.grenade_id++; + grenade.owner = self.unique_id; + grenade thread escape_artist_distance(); + grenade thread escape_artist(); + } +} + +escape_artist_distance() +{ + player = level.player; + id = self.unique_id; + while ( IsDefined( self ) ) + { + grenadeOffset = player.origin - self.origin; + // "+ 23" is compensating a bit for the player bounds + range = GetWeaponExplosionRadius( "fraggrenade" ) + 23; + + //range ^2 + rangeSq = squared( range ); + grenadeOffsetSq = LengthSquared(grenadeOffset); + + //Player outside range of grenade + if ( grenadeOffsetSq > rangeSq ) + { + if ( IsDefined(player.escape_artist[self.unique_id])) + player.escape_artist[self.unique_id] = undefined; + } + //Player in range + else + { + if ( !IsDefined( player.escape_artist[ self.unique_id ] ) ) + if (IsDefined( self.owner ) ) + player.escape_artist[ self.unique_id ] = true; + } + waitframe; + + } + if ( IsDefined( player.escape_artist[ id ] ) ) + player.escape_artist[ id ] = undefined; +} + +escape_artist() +{ + player = level.player; + while ( IsDefined( self ) ) + { + if (IsDefined( player.escape_artist[self.unique_id] ) ) + { + msg = level.player waittill_any_timeout( 4, "exo_dodge" ); + if ( IsDefined( msg ) && msg == "exo_dodge" ) + { + self thread check_grenade_dmg(); + } + } + else + waitframe; + } +} + +check_grenade_dmg() +{ + level.player endon( "death" ); + while ( IsDefined( self ) ) + { + msg = level.player waittill_dmg_timeout( 1, "damage" ); + //If the player takes damage + if ( IsDefined( msg ) && IsArray( msg ) ) + { + //If the player takes grenade damage + if ( msg[ 5 ] == "MOD_GRENADE_SPLASH" && !IsDefined( level.player.escape_artist_failure ) ) + //If the grenade damage was one that we were tracking and the player exo_dodged away + if ( msg[ 2 ].unique_id == self.owner && IsDefined( level.player.escape_artist[ self.unique_id ] ) ) + level.player.escape_artist_failure = true; + } + else + waitframe; + } + + if ( !IsDefined( level.player.escape_artist_failure ) ) + level.player escape_artist_success(); + else + level.player.escape_artist_failure = undefined; +} + +escape_artist_success() +{ + escapee = self GetLocalPlayerProfileData( "ach_escapeArtist" ) + 1; + if ( escapee == 20 ) + giveachievement_wrapper( "GRENADE_DODGE" ); + self SetLocalPlayerProfileData( "ach_escapeArtist", escapee ); +} + +waittill_dmg_timeout( timeOut, string1 ) +{ + if ( ( !IsDefined( string1 ) || string1 != "death" ) ) + self endon( "death" ); + + ent = SpawnStruct(); + + if ( isdefined( string1 ) ) + self childthread waittill_string_parms( string1, ent ); + + ent childthread _timeout( timeOut ); + + ent waittill( "returned", msg ); + ent notify( "die" ); + return msg; +} + +shock_ondeath() +{ + Assert( IsPlayer( self )); + + PreCacheShellShock( "default" ); + self waittill( "death" ); + + if ( IsDefined( self.specialDeath ) ) + return; + + if ( GetDvar( "r_texturebits" ) == "16" ) + return; + self ShellShock( "default", 3 ); +} + +/* +============= +///ScriptDocBegin +"Name: playerwatch_unresolved_collision()" +"Summary: Teleports the player to the closest pathnode if stuck in moving platform geo for at least a second." +"Module: Player" +"CallOn: Player" +"Example: level.player playerwatch_unresolved_collision()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +playerwatch_unresolved_collision() +{ + self endon( "death" ); + self endon( "stop_unresolved_collision_script" ); + + self reset_unresolved_collision_handler(); + self childthread playerwatch_unresolved_collision_count(); + + while( 1 ) + { + if ( self.unresolved_collision ) + { + self.unresolved_collision = false; + if ( self.unresolved_collision_count >= 20 ) + { + if ( IsDefined( self.handle_unresolved_collision ) ) + self [[self.handle_unresolved_collision]](); + else + self default_unresolved_collision_handler(); + } + } + else + { + self reset_unresolved_collision_handler(); + } + + wait 0.05; + } +} + +playerwatch_unresolved_collision_count() +{ + while( 1 ) + { + self waittill( "unresolved_collision" ); + self.unresolved_collision = true; + self.unresolved_collision_count++; + } +} + +reset_unresolved_collision_handler() +{ + self.unresolved_collision = false; + self.unresolved_collision_count = 0; +} + +default_unresolved_collision_handler() +{ + nodes = GetNodesInRadiusSorted( self.origin, 300, 0, 200, "Path" ); + if ( nodes.size ) + { + self CancelMantle(); + self DontInterpolate(); + self SetOrigin( nodes[0].origin ); + self reset_unresolved_collision_handler(); + } + else + { + PrintLn( "^3Warning! killing player for unresolved_collision, could not find pathnode." ); + self kill(); + } +} + +/* +============= +///ScriptDocBegin +"Name: stop_playerwatch_unresolved_collision()" +"Summary: Stops unresolved collision script for moving platforms." +"Module: Player" +"CallOn: Player" +"Example: level.player stop_playerwatch_unresolved_collision()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +stop_playerwatch_unresolved_collision() +{ + self notify( "stop_unresolved_collision_script" ); + self reset_unresolved_collision_handler(); +} + +/* +============= +///ScriptDocBegin +"Name: delete_on_death_wait_sound( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +delete_on_death_wait_sound( ent, sounddone ) +{ + ent endon( "death" ); + self waittill( "death" ); + if ( IsDefined( ent ) ) + { + if ( ent IsWaitingOnSound() ) + ent waittill( sounddone ); + + ent Delete(); + } +} + +is_dead_sentient() +{ + return IsSentient( self ) && !isalive( self ); +} + + /* + ============= +///ScriptDocBegin +"Name: play_sound_on_tag( , , )" +"Summary: Play the specified sound alias on a tag of an entity" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to play" +"OptionalArg: : Tag on the entity to play sound on. If no tag is specified the entities origin will be used." +"OptionalArg: : The sound will be cut short if the entity dies. Defaults to false." +"OptionalArg: : Optional notify string on sound done." +"Example: vehicle thread play_sound_on_tag( "horn_honk", "tag_engine" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_sound_on_tag( alias, tag, ends_on_death, op_notify_string, radio_dialog ) +{ + if ( is_dead_sentient() ) + return; + + if ( !SoundExists(alias) ) + return; + + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + org endon( "death" ); + + thread delete_on_death_wait_sound( org, "sounddone" ); + if ( IsDefined( tag ) ) + org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) ); + else + { + org.origin = self.origin; + org.angles = self.angles; + org LinkTo( self ); + } + + /# + if ( IsDefined( level.player_radio_emitter ) && ( self == level.player_radio_emitter ) ) + PrintLn( "**dialog alias playing radio: " + alias ); + #/ + + org PlaySound( alias, "sounddone" ); + if ( IsDefined( ends_on_death ) ) + { + AssertEx( ends_on_death, "ends_on_death must be true or undefined" ); + if ( !isdefined( wait_for_sounddone_or_death( org ) ) ) + org StopSounds(); // don't call StopSounds (redundantly) when the sound stopped since this can cut off sounds in SO for the non host + wait( 0.05 );// stopsounds doesnt work if the org is deleted same frame + } + else + { + org waittill( "sounddone" ); + } + if ( IsDefined( op_notify_string ) ) + self notify( op_notify_string ); + org Delete(); +} + + +/* +============= +///ScriptDocBegin +"Name: play_sound_on_tag_endon_death( , )" +"Summary: Play the specified sound alias on a tag of an entity but gets cut short if the entity dies" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to play" +"OptionalArg: : Tag on the entity to play sound on. If no tag is specified the entities origin will be used." +"Example: vehicle thread play_sound_on_tag_endon_death( "horn_honk", "tag_engine" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +play_sound_on_tag_endon_death( alias, tag ) +{ + play_sound_on_tag( alias, tag, true ); +} + + /* + ============= +///ScriptDocBegin +"Name: play_sound_on_entity( , )" +"Summary: Play the specified sound alias on an entity at it's origin" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to play" +"OptionalArg: : Optional notify string on sound done." +"Example: level.player play_sound_on_entity( "breathing_better" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_sound_on_entity( alias, op_notify_string ) +{ + AssertEx( !isSpawner( self ), "Spawner tried to play a sound" ); + + play_sound_on_tag( alias, undefined, undefined, op_notify_string ); +} + + /* + ============= +///ScriptDocBegin +"Name: play_loop_sound_on_tag( , , bStopSoundOnDeath, bStopSoundOnRemoved )" +"Summary: Play the specified looping sound alias on a tag of an entity" +"Module: Sound" +"CallOn: An entity" +"MandatoryArg: : Sound alias to loop" +"OptionalArg: : Tag on the entity to play sound on. If no tag is specified the entities origin will be used." +"OptionalArg: : Defaults to true. If true, will stop the looping sound when self dies" +"OptionalArg: : Defaults to false. If true, will stop the looping sound when self is undefined without dying. Safty measure to avoid infinite looping sound" +"Example: vehicle thread play_loop_sound_on_tag( "engine_belt_run", "tag_engine" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +play_loop_sound_on_tag( alias, tag, bStopSoundOnDeath, bStopSoundOnRemoved ) +{ + org = Spawn( "script_origin", ( 0, 0, 0 ) ); + org endon( "death" ); + + if ( !isdefined( bStopSoundOnDeath ) ) + bStopSoundOnDeath = true; + if ( bStopSoundOnDeath ) + thread delete_on_death( org ); + + if ( !isdefined( bStopSoundOnRemoved ) ) + bStopSoundOnRemoved = false; + if ( bStopSoundOnRemoved ) + thread delete_on_removed( org ); + + if ( IsDefined( tag ) ) + org LinkTo( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) ); + else + { + org.origin = self.origin; + org.angles = self.angles; + org LinkTo( self ); + } +// org endon( "death" ); + org PlayLoopSound( alias ); +// PrintLn( "playing loop sound ", alias, " on entity at origin ", self.origin, " at ORIGIN ", org.origin ); + self waittill( "stop sound" + alias ); + org StopLoopSound( alias ); + org Delete(); +} + +delete_on_removed( ent ) +{ + //self ==> the entity you want to wait to be undefined either by death or removed before deleting the ent + ent endon( "death" ); + + while( isdefined( self ) ) + wait 0.05; + + //self waittill( "death" ); + if ( IsDefined( ent ) ) + ent Delete(); +} + +save_friendlies() +{ + ai = GetAIArray( "allies" ); + game_characters = 0; + for ( i = 0; i < ai.size; i++ ) + { + if ( IsDefined( ai[ i ].script_friendname ) ) + continue; + +// attachsize = +// PrintLn( "attachSize = ", self GetAttachSize() ); + + game[ "character" + game_characters ] = ai[ i ] codescripts\character::save(); + game_characters++; + } + + game[ "total characters" ] = game_characters; +} + + /* + ============= +///ScriptDocBegin +"Name: spawn_failed( )" +"Summary: Checks to see if the spawned AI spawned correctly or had errors. Also waits until all spawn initialization is complete. Returns true or false." +"Module: AI" +"CallOn: " +"MandatoryArg: : The actor that just spawned" +"Example: spawn_failed( level.price );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +spawn_failed( spawn ) +{ + if ( !isalive( spawn ) ) + return true; + if ( !isdefined( spawn.finished_spawning ) ) + spawn waittill_either( "finished spawning", "death" ); + + if ( IsAlive( spawn ) ) + return false; + + return true; +} + +spawn_setcharacter( data ) +{ + codescripts\character::precache( data ); + + self waittill( "spawned", spawn ); + if ( maps\_utility::spawn_failed( spawn ) ) + return; + + PrintLn( "Size is ", data[ "attach" ].size ); + spawn codescripts\character::new(); + spawn codescripts\character::load( data ); +} + +key_hint_print( message, binding ) +{ + // Note that this will insert only the first bound key for the action + IPrintLnBold( message, binding[ "key1" ] ); +} + +view_tag( tag ) +{ + self endon( "death" ); + for ( ;; ) + { + maps\_debug::drawTag( tag ); + wait( 0.05 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: assign_animtree( )" +"Summary: Assigns the level.scr_animtree for the given animname to self." +"Module: Anim" +"OptionalArg: : You can optionally assign the animname for self at this juncture." +"Example: model = assign_animtree( "whatever" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +assign_animtree( animname ) +{ + +/* +============= +///ScriptFieldDocBegin +"Name: .animname" +"Summary: animname" +"Module: Utility" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + if ( IsDefined( animname ) ) + self.animname = animname; + + AssertEx( IsDefined( level.scr_animtree[ self.animname ] ), "There is no level.scr_animtree for animname " + self.animname ); + self UseAnimTree( level.scr_animtree[ self.animname ] ); +} + +/* +============= +///ScriptDocBegin +"Name: assign_model( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +assign_model() +{ + AssertEx( IsDefined( level.scr_model[ self.animname ] ), "There is no level.scr_model for animname " + self.animname ); + + if ( IsArray( level.scr_model[ self.animname ] ) ) + { + randIndex = RandomInt( level.scr_model[ self.animname ].size ); + self SetModel( level.scr_model[ self.animname ][ randIndex ] ); + } + else + self SetModel( level.scr_model[ self.animname ] ); +} + + /* + ============= +///ScriptDocBegin +"Name: spawn_anim_model( , )" +"Summary: Spawns a script model and gives it the animtree and model associated with that animname" +"Module: Anim" +"MandatoryArg: : Name of the animname from this map_anim.gsc." +"OptionalArg: : Optional origin." +"Example: model = spawn_anim_model( "player_rappel" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +spawn_anim_model( animname, origin, angles ) +{ + if ( !IsDefined( origin ) ) + origin = ( 0, 0, 0 ); + + model = Spawn( "script_model", origin ); + model.animname = animname; + model assign_animtree(); + model assign_model(); + if ( IsDefined( angles ) ) + model.angles = angles; + return model; +} + + /* + ============= +///ScriptDocBegin +"Name: trigger_wait( , )" +"Summary: Waits until a trigger with the specified key / value is triggered" +"Module: Trigger" +"CallOn: " +"MandatoryArg: : Name of the key on this trigger" +"MandatoryArg: : Key on the trigger to use, example: "targetname" or "script_noteworthy"" +"Example: trigger_wait( "player_in_building1", "targetname" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +trigger_wait( strName, strKey ) +{ + eTrigger = GetEnt( strName, strKey ); + if ( !isdefined( eTrigger ) ) + { + AssertMsg( "trigger not found: " + strName + " key: " + strKey ); + return; + } + eTrigger waittill( "trigger", eOther ); + level notify( strName, eOther ); + return eOther; +} + /* + ============= +///ScriptDocBegin +"Name: trigger_wait_targetname( )" +"Summary: Waits until a trigger with the specified key / value is triggered" +"Module: Trigger" +"CallOn: " +"MandatoryArg: : targetname key to waitfor" +"Example: trigger_wait_targetname( "player_in_building1" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +trigger_wait_targetname( strName ) +{ + return trigger_wait( strName, "targetname" ); +} + +/* +============= +///ScriptDocBegin +"Name: set_flag_on_dead( , )" +"Summary: the function will set the flag after the actors from have spawned and then died " +"Module: AI" +"CallOn: " +"MandatoryArg: : the array of spawners" +"MandatoryArg: : the flag to set" +"Example: set_flag_on_dead( spawners, "base_guys_dead" )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_flag_on_dead( spawners, strFlag ) +{ + thread set_flag_on_func_wait_proc( spawners, strFlag, ::waittill_dead, "set_flag_on_dead" ); +} + +/* +============= +///ScriptDocBegin +"Name: set_flag_on_dead_or_dying( , )" +"Summary: the function will set the flag after the actors from have spawned and then are dead or dying ( long death )." +"Module: AI" +"CallOn: " +"MandatoryArg: : the array of spawners" +"MandatoryArg: : the flag to set" +"Example: set_flag_on_dead_or_dying( spawners, "base_guys_dead" )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_flag_on_dead_or_dying( spawners, strFlag ) +{ + thread set_flag_on_func_wait_proc( spawners, strFlag, ::waittill_dead_or_dying, "set_flag_on_dead_or_dying" ); +} + +/* +============= +///ScriptDocBegin +"Name: set_flag_on_spawned( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_flag_on_spawned( spawners, strFlag ) +{ + thread set_flag_on_func_wait_proc( spawners, strFlag, ::empty_func, "set_flag_on_spawned" ); +} + +empty_func( var ) +{ + return; +} + +set_flag_on_spawned_ai_proc( system, internal_flag ) +{ + self waittill( "spawned", guy ); + if ( maps\_utility::spawn_failed( guy ) ) + return; + + system.ai[ system.ai.size ] = guy; + + self ent_flag_set( internal_flag ); +} + +set_flag_on_func_wait_proc( spawners, strFlag, waitfunc, internal_flag ) +{ + system = SpawnStruct(); + system.ai = []; + + AssertEx( spawners.size, "spawners is empty" ); + + foreach ( key, spawn in spawners ) + spawn ent_flag_init( internal_flag ); + + array_thread( spawners, ::set_flag_on_spawned_ai_proc, system, internal_flag ); + + foreach ( key, spawn in spawners ) + spawn ent_flag_wait( internal_flag ); + + [[ waitfunc ]]( system.ai ); + flag_set( strFlag ); +} + + /* + ============= +///ScriptDocBegin +"Name: set_flag_on_trigger( , )" +"Summary: Calls flag_set to set the specified flag when the trigger is triggered" +"Module: Trigger" +"CallOn: " +"MandatoryArg: : trigger entity to use" +"MandatoryArg: : name of the flag to set" +"Example: set_flag_on_trigger( trig, "player_is_outside" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +set_flag_on_trigger( eTrigger, strFlag ) +{ + if ( !flag( strFlag ) ) + { + eTrigger waittill( "trigger", eOther ); + flag_set( strFlag ); + return eOther; + } +} + + /* + ============= +///ScriptDocBegin +"Name: set_flag_on_targetname_trigger( )" +"Summary: Sets the specified flag when a trigger with targetname < flag > is triggered." +"Module: Trigger" +"CallOn: " +"MandatoryArg: : name of the flag to set, and also the targetname of the trigger to use" +"Example: set_flag_on_targetname_trigger( "player_is_outside" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +set_flag_on_targetname_trigger( msg ) +{ + Assert( IsDefined( level.flag[ msg ] ) ); + if ( flag( msg ) ) + return; + + trigger = GetEnt( msg, "targetname" ); + trigger waittill( "trigger" ); + flag_set( msg ); +} + + /* + ============= +///ScriptDocBegin +"Name: is_in_array( , )" +"Summary: Returns true if < eFindee > is an entity in array < aeCollection > . False if it is not. " +"Module: Array" +"CallOn: " +"MandatoryArg: : array of entities to search through" +"MandatoryArg: : entity to check if it's in the array" +"Example: qBool = is_in_array( eTargets, vehicle1 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +is_in_array( aeCollection, eFindee ) +{ + for ( i = 0; i < aeCollection.size; i++ ) + { + if ( aeCollection[ i ] == eFindee ) + return( true ); + } + + return( false ); +} + + /* + ============= +///ScriptDocBegin +"Name: waittill_dead( , , )" +"Summary: Waits until all the AI in array < guys > are dead." +"Module: AI" +"CallOn: " +"MandatoryArg: : Array of actors to wait until dead" +"OptionalArg: : Number of guys that must die for this function to continue" +"OptionalArg: : Number of seconds before this function times out and continues" +"Example: waittill_dead( GetAIArray( "axis" ) );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +waittill_dead( guys, num, timeoutLength ) +{ + // verify the living - ness of the ai + /# + allAlive = true; + foreach ( member in guys ) + { + if ( IsAlive( member ) ) + continue; + allAlive = false; + break; + } + AssertEx( allAlive, "Waittill_Dead was called with dead or removed AI in the array, meaning it will never pass." ); + if ( !allAlive ) + { + newArray = []; + foreach ( member in guys ) + { + if ( IsAlive( member ) ) + newArray[ newArray.size ] = member; + } + guys = newArray; + } + #/ + + ent = SpawnStruct(); + if ( IsDefined( timeoutLength ) ) + { + ent endon( "thread_timed_out" ); + ent thread waittill_dead_timeout( timeoutLength ); + } + + ent.count = guys.size; + if ( IsDefined( num ) && num < ent.count ) + ent.count = num; + array_thread( guys, ::waittill_dead_thread, ent ); + + while ( ent.count > 0 ) + ent waittill( "waittill_dead guy died" ); +} + + /* + ============= +///ScriptDocBegin +"Name: waittill_dead_or_dying( , , )" +"Summary: Similar to waittill_dead(). Waits until all the AI in array < guys > are dead OR dying (long deaths)." +"Module: AI" +"CallOn: " +"MandatoryArg: : Array of actors to wait until dead or dying" +"OptionalArg: : Number of guys that must die or be dying for this function to continue" +"OptionalArg: : Number of seconds before this function times out and continues" +"Example: waittill_dead_or_dying( GetAIArray( "axis" ) );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +waittill_dead_or_dying( guys, num, timeoutLength ) +{ + // verify the living - ness and healthy - ness of the ai + newArray = []; + foreach ( member in guys ) + { + if ( IsAlive( member ) && !member.ignoreForFixedNodeSafeCheck ) + newArray[ newArray.size ] = member; + } + guys = newArray; + + ent = SpawnStruct(); + if ( IsDefined( timeoutLength ) ) + { + ent endon( "thread_timed_out" ); + ent thread waittill_dead_timeout( timeoutLength ); + } + + ent.count = guys.size; + + // optional override on count + if ( IsDefined( num ) && num < ent.count ) + ent.count = num; + + array_thread( guys, ::waittill_dead_or_dying_thread, ent ); + + while ( ent.count > 0 ) + ent waittill( "waittill_dead_guy_dead_or_dying" ); +} + +waittill_dead_thread( ent ) +{ + self waittill( "death" ); + ent.count--; + ent notify( "waittill_dead guy died" ); +} + +waittill_dead_or_dying_thread( ent ) +{ + self waittill_either( "death", "pain_death" ); + ent.count--; + ent notify( "waittill_dead_guy_dead_or_dying" ); +} + +waittill_dead_timeout( timeoutLength ) +{ + wait( timeoutLength ); + self notify( "thread_timed_out" ); +} + +waittill_aigroupcleared( aigroup ) +{ + while ( level._ai_group[ aigroup ].spawnercount || level._ai_group[ aigroup ].aicount ) + wait( 0.25 ); +} + +waittill_aigroupcount( aigroup, count ) +{ + while ( level._ai_group[ aigroup ].spawnercount + level._ai_group[ aigroup ].aicount > count ) + wait( 0.25 ); +} + + +get_ai_group_count( aigroup ) +{ + return( level._ai_group[ aigroup ].spawnercount + level._ai_group[ aigroup ].aicount ); +} + +get_ai_group_sentient_count( aigroup ) +{ + return( level._ai_group[ aigroup ].aicount ); +} + +get_ai_group_ai( aigroup ) +{ + aiSet = []; + for ( index = 0; index < level._ai_group[ aigroup ].ai.size; index++ ) + { + if ( !isAlive( level._ai_group[ aigroup ].ai[ index ] ) ) + continue; + + aiSet[ aiSet.size ] = level._ai_group[ aigroup ].ai[ index ]; + } + + return( aiSet ); +} + + /* + ============= +///ScriptDocBegin +"Name: waittill_notetrack_or_damage( )" +"Summary: Waits until the entity hits a certain notetrack or is damaged or killed" +"Module: AI" +"CallOn: An AI" +"MandatoryArg: : the notetrack to wait for" +"Example: self waittill_notetrack_or_damage( "bodyfall large" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +waittill_notetrack_or_damage( notetrack ) +{ + self endon( "damage" ); + self endon( "death" ); + self waittillmatch( "single anim", notetrack ); +} + + /* + ============= +///ScriptDocBegin +"Name: get_living_ai( , )" +"Summary: Returns single spawned ai in the level of and . Error if used on more than one ai with same name and type " +"Module: AI" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the ai" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"Example: patroller = get_living_ai( "patrol", "script_noteworthy" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_living_ai( name, type ) +{ + array = get_living_ai_array( name, type ); + if ( array.size > 1 ) + { + AssertMsg( "get_living_ai used for more than one living ai of type " + type + " called " + name + "." ); + return undefined; + } + return array[ 0 ]; +} + + /* + ============= +///ScriptDocBegin +"Name: get_living_ai_array( , )" +"Summary: Returns array of spawned ai in the level of and " +"Module: AI" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the ai" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"Example: patrollers = get_living_ai_array( "patrol", "script_noteworthy" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_living_ai_array( name, type ) +{ + ai = GetAISpeciesArray( "all", "all" ); + + array = []; + foreach ( actor in ai ) + { + if ( !IsAlive( actor ) ) + continue; + + switch( type ) + { + case "targetname":{ + if ( IsDefined( actor.targetname ) && actor.targetname == name ) + array[ array.size ] = actor; + }break; + case "script_noteworthy":{ + if ( IsDefined( actor.script_noteworthy ) && actor.script_noteworthy == name ) + array[ array.size ] = actor; + }break; + } + } + return array; +} + /* + ============= +///ScriptDocBegin +"Name: get_vehicle( , )" +"Summary: Returns the vehicle spawned from the SHIFT-V radient menu of and " +"Module: Vehicles" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the vehicle" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"Example: patrollers = get_vehicle( "heli_1", "script_noteworthy" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_vehicle( name, type ) +{ + Assert( IsDefined( name ) ); + Assert( IsDefined( type ) ); + array = get_vehicle_array( name, type ); + if ( !array.size ) + return undefined; + + AssertEx( array.size == 1, "tried to get_vehicle() on vehicles with key-pair: " + name + "," + type ); + return array[ 0 ]; +} + + /* + ============= +///ScriptDocBegin +"Name: get_vehicle_array( , )" +"Summary: Returns the vehicles spawned from the SHIFT-V radient menu of and " +"Module: Vehicles" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the vehicle" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"Example: helis = get_vehicle_array( "tower_helis", "script_noteworthy" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_vehicle_array( name, type ) +{ + array = GetEntArray( name, type ); + vehicle = []; + + merge_array = []; + + foreach ( object in array ) + { + if ( object.code_classname != "script_vehicle" ) + continue; + merge_array[ 0 ] = object; + //since vehicles lose their targetname return the last vehicle spawned from the spawner. + if ( IsSpawner( object ) ) + { + if ( IsDefined( object.last_spawned_vehicle ) ) + { + merge_array[ 0 ] = object.last_spawned_vehicle; + vehicle = array_merge( vehicle, merge_array );// least tricky way for me to do this. + } + continue; + } + vehicle = array_merge( vehicle, merge_array );// least tricky way for me to do this. + } + return vehicle; +} + /* + ============= +///ScriptDocBegin +"Name: get_living_aispecies( , , )" +"Summary: Returns single spawned ai in the level of and . Error if used on more than one ai with same name and type " +"Module: AI" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the ai" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"OptionalArg: : the breadof spieces, if none is given, defaults to 'all' " +"Example: patroller = get_living_aispecies( "patrol", "script_noteworthy", "dog" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_living_aispecies( name, type, breed ) +{ + array = get_living_aispecies_array( name, type, breed ); + if ( array.size > 1 ) + { + AssertMsg( "get_living_aispecies used for more than one living ai of type " + type + " called " + name + "." ); + return undefined; + } + return array[ 0 ]; +} + + /* + ============= +///ScriptDocBegin +"Name: get_living_aispecies_array( , , )" +"Summary: Returns array of spawned ai of any speices in the level of , , and " +"Module: AI" +"CallOn: " +"MandatoryArg: : the value of the targetname or script_noteworthy of the ai" +"MandatoryArg: : valid types are targetname and script_noteworthy" +"OptionalArg: : the breadof spieces, if none is given, defaults to 'all' " +"Example: patrollers = get_living_aispecies_array( "patrol", "script_noteworthy", "dog" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_living_aispecies_array( name, type, breed ) +{ + if ( !isdefined( breed ) ) + breed = "all"; + + ai = GetAISpeciesArray( "allies", breed ); + ai = array_combine( ai, GetAISpeciesArray( "axis", breed ) ); + + array = []; + for ( i = 0; i < ai.size; i++ ) + { + switch( type ) + { + case "targetname":{ + if ( IsDefined( ai[ i ].targetname ) && ai[ i ].targetname == name ) + array[ array.size ] = ai[ i ]; + }break; + case "script_noteworthy":{ + if ( IsDefined( ai[ i ].script_noteworthy ) && ai[ i ].script_noteworthy == name ) + array[ array.size ] = ai[ i ]; + }break; + } + } + return array; +} + +// Creates an event based on this message if none exists, and sets it to true after the delay. +gather_delay_proc( msg, delay ) +{ + if ( IsDefined( level.gather_delay[ msg ] ) ) + { + if ( level.gather_delay[ msg ] ) + { + wait( 0.05 ); + if ( IsAlive( self ) ) + self notify( "gather_delay_finished" + msg + delay ); + return; + } + + level waittill( msg ); + if ( IsAlive( self ) ) + self notify( "gather_delay_finished" + msg + delay ); + return; + } + + level.gather_delay[ msg ] = false; + wait( delay ); + level.gather_delay[ msg ] = true; + level notify( msg ); + if ( IsAlive( self ) ) + self notify( "gat her_delay_finished" + msg + delay ); +} + +gather_delay( msg, delay ) +{ + thread gather_delay_proc( msg, delay ); + self waittill( "gather_delay_finished" + msg + delay ); +} + +death_waiter( notifyString ) +{ + self waittill( "death" ); + level notify( notifyString ); +} + +getchar( num ) +{ + if ( num == 0 ) + return "0"; + if ( num == 1 ) + return "1"; + if ( num == 2 ) + return "2"; + if ( num == 3 ) + return "3"; + if ( num == 4 ) + return "4"; + if ( num == 5 ) + return "5"; + if ( num == 6 ) + return "6"; + if ( num == 7 ) + return "7"; + if ( num == 8 ) + return "8"; + if ( num == 9 ) + return "9"; +} + +getlinks_array( array, linkMap )// don't pass stuff through as an array of struct.linkname[] but only linkMap[] +{ + ents = []; + for ( j = 0; j < array.size; j++ ) + { + node = array[ j ]; + script_linkname = node.script_linkname; + if ( !isdefined( script_linkname ) ) + continue; + if ( !isdefined( linkMap[ script_linkname ] ) ) + continue; + ents[ ents.size ] = node; + } + return ents; +} + +// Adds only things that are new to the array. +// Requires the arrays to be of node with script_linkname defined. +/* +============= +///ScriptDocBegin +"Name: array_merge_links( , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_merge_links( array1, array2 ) +{ + if ( !array1.size ) + return array2; + if ( !array2.size ) + return array1; + + linkMap = []; + + for ( i = 0; i < array1.size; i++ ) + { + node = array1[ i ]; + linkMap[ node.script_linkName ] = true; + } + + for ( i = 0; i < array2.size; i++ ) + { + node = array2[ i ]; + if ( IsDefined( linkMap[ node.script_linkName ] ) ) + continue; + linkMap[ node.script_linkName ] = true; + array1[ array1.size ] = node; + } + + return array1; +} + + /* + ============= +///ScriptDocBegin +"Name: array_merge( , )" +"Summary: Combines the two arrays and returns the resulting array. Adds only things that are new to the array, no duplicates." +"Module: Array" +"CallOn: " +"MandatoryArg: : first array" +"MandatoryArg: : second array" +"Example: combinedArray = array_merge( array1, array2 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +array_merge( array1, array2 )// adds only things that are new to the array +{ + if ( array1.size == 0 ) + return array2; + if ( array2.size == 0 ) + return array1; + newarray = array1; + foreach ( array2_ent in array2 ) + { + foundmatch = false; + + foreach ( array1_ent in array1 ) + { + if ( array1_ent == array2_ent ) + { + foundmatch = true; + break; + } + } + if ( foundmatch ) + continue; + else + newarray[ newarray.size ] = array2_ent; + } + return newarray; +} + + /* + ============= +///ScriptDocBegin +"Name: array_exclude( , )" +"Summary: Returns an array excluding all members of < arrayExclude > " +"Module: Array" +"CallOn: " +"MandatoryArg: : Array containing all items" +"MandatoryArg: : Arary containing all items to remove" +"Example: newArray = array_exclude( array1, array2 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +array_exclude( array, arrayExclude )// returns "array" minus all members of arrayExclude +{ + newarray = array; + for ( i = 0; i < arrayExclude.size; i++ ) + { + if ( is_in_array( array, arrayExclude[ i ] ) ) + newarray = array_remove( newarray, arrayExclude[ i ] ); + } + + return newarray; +} + +/* +============= +///ScriptDocBegin +"Name: array_compare( , )" +"Summary: checks whether 2 arrays are equal. Returns true if they are, false if they are not" +"Module: Array" +"CallOn: " +"MandatoryArg: : the first array to compare " +"MandatoryArg: : the second array to compare " +"Example: if( array_compare( array1, array2 ) )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_compare( array1, array2 ) +{ + if ( array1.size != array2.size ) + return false; + + foreach ( key, member in array1 ) + { + if ( !isdefined( array2[ key ] ) ) + return false; + + member2 = array2[ key ]; + + if ( member2 != member ) + return false; + } + + return true; +} + +/* + ============= +///ScriptDocBegin + +"Name: getLinkedVehicleNodes()" +"Summary: Returns an array of vehicle nodes that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to vehicle nodes" +"Example: spawners = heli get_linked_ents()" +"SPMP: both" +///ScriptDocEnd +============= +*/ +getLinkedVehicleNodes() +{ + array = []; + + if ( IsDefined( self.script_linkTo ) ) + { + linknames = get_links(); + foreach ( name in linknames ) + { + entities = GetVehicleNodeArray( name, "script_linkname" ); + array = array_combine( array, entities ); + } + } + + return array; +} + + + + + /* + ============= +///ScriptDocBegin +"Name: draw_line( , , , , )" +"Summary: Draws a line from < org1 > to < org2 > in the specified color forever" +"Module: Debug" +"CallOn: " +"MandatoryArg: : starting origin for the line" +"MandatoryArg: : ending origin for the line" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"Example: thread draw_line( level.player.origin, vehicle.origin, 1, 0, 0 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line( org1, org2, r, g, b ) +{ + while ( 1 ) + { + Line( org1, org2, ( r, g, b ), 1 ); + wait .05; + } + +} + + /* + ============= +///ScriptDocBegin +"Name: draw_line_to_ent_for_time( , , , , , )" +"Summary: Draws a line from < org1 > to < ent > origin in the specified color for the specified duration. Updates to the entities origin each frame." +"Module: Debug" +"CallOn: " +"MandatoryArg: : starting origin for the line" +"MandatoryArg: : entity to draw line to" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : time in seconds the line should last" +"Example: thread draw_line_to_ent_for_time( level.player.origin, vehicle, 1, 0, 0, 10.0 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line_to_ent_for_time( org1, ent, r, g, b, timer ) +{ + timer = GetTime() + ( timer * 1000 ); + while ( GetTime() < timer ) + { + Line( org1, ent.origin, ( r, g, b ), 1 ); + wait .05; + if ( !isdefined( ent ) || !isdefined( ent.origin ) ) + return; + } + +} + +/* +============= +///ScriptDocBegin +"Name: draw_line_from_ent_for_time( , , , , , )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +draw_line_from_ent_for_time( ent, org, r, g, b, timer ) +{ + draw_line_to_ent_for_time( org, ent, r, g, b, timer ); +} + + /* + ============= +///ScriptDocBegin +"Name: draw_line_from_ent_to_ent_for_time( , , , , , )" +"Summary: Draws a line from one entity origin to another entity origin in the specified color for the specified duration. Updates to the entities origin each frame." +"Module: Debug" +"CallOn: " +"MandatoryArg: : entity to draw line from" +"MandatoryArg: : entity to draw line to" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : time in seconds the line should last" +"Example: thread draw_line_from_ent_to_ent_for_time( level.player, vehicle, 1, 0, 0, 10.0 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line_from_ent_to_ent_for_time( ent1, ent2, r, g, b, timer ) +{ + ent1 endon( "death" ); + ent2 endon( "death" ); + + timer = GetTime() + ( timer * 1000 ); + while ( GetTime() < timer ) + { + Line( ent1.origin, ent2.origin, ( r, g, b ), 1 ); + wait .05; + } + +} + + /* + ============= +///ScriptDocBegin +"Name: draw_line_from_ent_to_ent_until_notify( , , , , , , )" +"Summary: Draws a line from one entity origin to another entity origin in the specified color until < notifyEnt > is notified < notifyString > . Updates to the entities origin each frame." +"Module: Debug" +"CallOn: " +"MandatoryArg: : entity to draw line from" +"MandatoryArg: : entity to draw line to" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : entity that waits for the notify" +"MandatoryArg: : notify string that will make the line stop being drawn" +"Example: thread draw_line_from_ent_to_ent_until_notify( level.player, guy, 1, 0, 0, guy, "anim_on_tag_done" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line_from_ent_to_ent_until_notify( ent1, ent2, r, g, b, notifyEnt, notifyString ) +{ + Assert( IsDefined( notifyEnt ) ); + Assert( IsDefined( notifyString ) ); + + ent1 endon( "death" ); + ent2 endon( "death" ); + + notifyEnt endon( notifyString ); + + while ( 1 ) + { + Line( ent1.origin, ent2.origin, ( r, g, b ), 0.05 ); + wait .05; + } + +} + + /* + ============= +///ScriptDocBegin +"Name: draw_line_until_notify( , , , , , , )" +"Summary: Draws a line from < org1 > to < org2 > in the specified color until < notifyEnt > is notified < notifyString > " +"Module: Debug" +"CallOn: " +"MandatoryArg: : starting origin for the line" +"MandatoryArg: : ending origin for the line" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : entity that waits for the notify" +"MandatoryArg: : notify string that will make the line stop being drawn" +"Example: thread draw_line_until_notify( self.origin, targetLoc, 1, 0, 0, self, "stop_drawing_line" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line_until_notify( org1, org2, r, g, b, notifyEnt, notifyString ) +{ + Assert( IsDefined( notifyEnt ) ); + Assert( IsDefined( notifyString ) ); + + notifyEnt endon( notifyString ); + + while ( 1 ) + { + draw_line_for_time( org1, org2, r, g, b, 0.05 ); + } +} + + /* + ============= +///ScriptDocBegin +"Name: draw_line_from_ent_to_vec_for_time( , , , , , , )" +"Summary: Draws a line from < org1 > to < ent > origin in the specified color for the specified duration. Updates to the entities origin each frame." +"Module: Debug" +"CallOn: " +"MandatoryArg: : entity to draw line to" +"MandatoryArg: : vector to draw the line at" +"MandatoryArg: : length of the vector line" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : time in seconds the line should last" +"Example: thread draw_line_from_ent_to_vec_for_time( vehicle, ( 0, 1, 0 ), 10, 1, 0, 0, 10.0 );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_line_from_ent_to_vec_for_time( ent, vec, length, r, g, b, timer ) +{ + timer = GetTime() + ( timer * 1000 ); + vec *= length; + while ( GetTime() < timer ) + { + Line( ent.origin, ent.origin + vec, ( r, g, b ), 1 ); + wait .05; + if ( !isdefined( ent ) || !isdefined( ent.origin ) ) + return; + } +} + + /* + ============= +///ScriptDocBegin +"Name: draw_circle_until_notify(
, , , , , , )" +"Summary: Draws a circle at < center > with < radius > in the specified color until < notifyEnt > is notified < notifyString > " +"Module: Debug" +"CallOn: " +"MandatoryArg:
: origin center of the circle" +"MandatoryArg: : radius of the circle to draw" +"MandatoryArg: : red color value( 0 to 1 )" +"MandatoryArg: : green color value( 0 to 1 )" +"MandatoryArg: : blue color value( 0 to 1 )" +"MandatoryArg: : entity that waits for the notify" +"MandatoryArg: : notify string that will make the line stop being drawn" +"Example: thread draw_circle_until_notify( self.origin, self.radius, 1, 0, 0, self, "stop_drawing_circle" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +draw_circle_until_notify( center, radius, r, g, b, notifyEnt, notifyString, optionalSides ) +{ + if( IsDefined(optionalSides)) + circle_sides = optionalSides; + else + circle_sides = 16; + + angleFrac = 360 / circle_sides; + + // Z circle + circlepoints = []; + for ( i = 0; i < circle_sides; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + xAdd; + y = center[ 1 ] + yAdd; + z = center[ 2 ]; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread draw_circle_lines_until_notify( circlepoints, r, g, b, notifyEnt, notifyString ); + /* + // X circle + circlepoints = []; + for ( i = 0 ; i < circle_sides ; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ]; + y = center[ 1 ] + xAdd; + z = center[ 2 ] + yAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + + // Y circle + circlepoints = []; + for ( i = 0 ; i < circle_sides ; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + yAdd; + y = center[ 1 ]; + z = center[ 2 ] + xAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + */ +} + +draw_circle_for_time( center, radius, r, g, b, time ) +{ + circle_sides = 16; + + angleFrac = 360 / circle_sides; + + // Z circle + circlepoints = []; + for ( i = 0; i < circle_sides; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + xAdd; + y = center[ 1 ] + yAdd; + z = center[ 2 ]; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread draw_circle_lines_for_time( circlepoints, r, g, b, time ); + /* + // X circle + circlepoints = []; + for ( i = 0 ; i < circle_sides ; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ]; + y = center[ 1 ] + xAdd; + z = center[ 2 ] + yAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + + // Y circle + circlepoints = []; + for ( i = 0 ; i < circle_sides ; i++ ) + { + angle = ( angleFrac * i ); + xAdd = Cos( angle ) * radius; + yAdd = Sin( angle ) * radius; + x = center[ 0 ] + yAdd; + y = center[ 1 ]; + z = center[ 2 ] + xAdd; + circlepoints[ circlepoints.size ] = ( x, y, z ); + } + thread debug_circle_drawlines( circlepoints, 5.0, ( 1, 0, 0 ), center ); + */ +} + +draw_circle_lines_for_time( circlepoints, r, g, b, time) +{ + for ( i = 0; i < circlepoints.size; i++ ) + { + start = circlepoints[ i ]; + if ( i + 1 >= circlepoints.size ) + end = circlepoints[ 0 ]; + else + end = circlepoints[ i + 1 ]; + + thread draw_line_for_time( start, end, r, g, b, time ); + } +} + +draw_circle_lines_until_notify( circlepoints, r, g, b, notifyEnt, notifyString ) +{ + for ( i = 0; i < circlepoints.size; i++ ) + { + start = circlepoints[ i ]; + if ( i + 1 >= circlepoints.size ) + end = circlepoints[ 0 ]; + else + end = circlepoints[ i + 1 ]; + + thread draw_line_until_notify( start, end, r, g, b, notifyEnt, notifyString ); + } +} + +clear_enemy_passthrough() +{ + self notify( "enemy" ); + self ClearEnemy(); +} + + /* + ============= +///ScriptDocBegin +"Name: battlechatter_off( )" +"Summary: Disable battlechatter for the specified team" +"Module: Battlechatter" +"CallOn: " +"MandatoryArg: : team to disable battlechatter on" +"Example: battlechatter_off( "allies" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +battlechatter_off( team ) +{ + level notify( "battlechatter_off_thread" ); + + maps\_dds::dds_disable( team ); + + animscripts\battlechatter::bcs_setup_chatter_toggle_array(); + + if ( IsDefined( team ) ) + { + set_battlechatter_variable( team, false ); + soldiers = GetAIArray( team ); + } + else + { + foreach ( team in anim.teams ) + { + set_battlechatter_variable( team, false ); + } + + soldiers = GetAIArray(); + } + + if ( !isDefined( anim.chatInitialized ) || !anim.chatInitialized ) + return; + + for ( index = 0; index < soldiers.size; index++ ) + soldiers[ index ].battlechatter = false; + + for ( index = 0; index < soldiers.size; index++ ) + { + soldier = soldiers[ index ]; + if ( !isalive( soldier ) ) + continue; + + if ( !soldier.chatInitialized ) + continue; + + if ( !soldier.isSpeaking ) + continue; + + soldier wait_until_done_speaking(); + } + + speakDiff = GetTime() - anim.lastTeamSpeakTime[ "allies" ]; + + if ( speakDiff < 1500 ) + wait( speakDiff / 1000 ); + + if ( IsDefined( team ) ) + level notify( team + " done speaking" ); + else + level notify( "done speaking" ); +} + + /* + ============= +///ScriptDocBegin +"Name: battlechatter_on( )" +"Summary: Enable battlechatter for the specified team" +"Module: Battlechatter" +"CallOn: " +"MandatoryArg: : team to enable battlechatter on" +"Example: battlechatter_on( "allies" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +battlechatter_on( team ) +{ + thread battlechatter_on_thread( team ); + + maps\_dds::dds_enable( team ); +} + +battlechatter_on_thread( team ) +{ + level endon( "battlechatter_off_thread" ); + + animscripts\battlechatter::bcs_setup_chatter_toggle_array(); + + while ( !IsDefined( anim.chatInitialized ) ) + { + wait( 0.05 ); + } + + flag_set( "battlechatter_on_thread_waiting" ); + + // buffer time + wait( 1.5 ); + + flag_clear( "battlechatter_on_thread_waiting" ); + + if ( IsDefined( team ) ) + { + set_battlechatter_variable( team, true ); + soldiers = GetAIArray( team ); + } + else + { + foreach ( team in anim.teams ) + { + set_battlechatter_variable( team, true ); + } + soldiers = GetAIArray(); + } + + for ( index = 0; index < soldiers.size; index++ ) + soldiers[ index ] set_battlechatter( true ); +} + +/* +============= +///ScriptDocBegin +"Name: set_battlechatter( )" +"Summary: Turns an AI's battlechatter on/off. Will be overridden if a spawner's .script_bcdialog is set to 0." +"Module: Battlechatter" +"CallOn: An AI" +"MandatoryArg: : True/false, describes whether battlechatter should be on or off for this AI" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_battlechatter( bEnable ) +{ + dds_exclude_this_ai( !bEnable ); + + if ( !IsDefined(anim.chatInitialized) || !anim.chatInitialized ) + return; + + if ( self.type == "dog" ) + return; + + if ( bEnable ) + { + if ( IsDefined( self.script_bcdialog ) && !self.script_bcdialog ) + self.battlechatter = false; + else + self.battlechatter = true; + } + else + { + self.battlechatter = false; + + if ( IsDefined( self.isSpeaking ) && self.isSpeaking ) + self waittill( "done speaking" ); + } +} +/* +============= +///ScriptDocBegin +"Name: set_team_bcvoice( , )" +"Summary: Changes all the battlechatter voices for a certain team. Useful for switching between loud/stealth chatter." +"Module: Battlechatter" +"CallOn: " +"MandatoryArg: : Allies/Axis/Team3, which team should change voices" +"MandatoryArg: : What voice should they use. See 'anim.countryIDs' keys for valid voices" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_team_bcvoice( team, newvoice ) +{ + if ( !anim.chatInitialized ) + return; + + supported_voicetypes = GetArrayKeys( anim.countryIDs ); + in_supported_voicetypes = array_contains( supported_voicetypes, newvoice ); + assertEx( in_supported_voicetypes, "Tried to change ai's voice to " + newvoice + " but that voicetype is not supported!" ); + + if ( !in_supported_voicetypes ) + return; + + allies = GetAIArray( team ); + foreach( ai in allies ) + { + ai set_ai_bcvoice( newvoice ); + waitframe(); // watiframe so it doesn't hitch + } +} + +/* +============= +///ScriptDocBegin +"Name: set_ai_bcvoice( )" +"Summary: Changes all the battlechatter voices for a single ai. Useful for switching between loud/stealth chatter." +"Module: Battlechatter" +"CallOn: an AI" +"MandatoryArg: : What voice should they use. See 'anim.countryIDs' keys for valid voices" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_ai_bcvoice( newvoice ) +{ + if ( !anim.chatInitialized ) + return; + + supported_voicetypes = GetArrayKeys( anim.countryIDs ); + in_supported_voicetypes = array_contains( supported_voicetypes, newvoice ); + assertEx( in_supported_voicetypes, "Tried to change ai's voice to " + newvoice + " but that voicetype is not supported!" ); + + if ( !in_supported_voicetypes ) + return; + + if ( self.type == "dog" ) + return; + + if ( IsDefined( self.isSpeaking ) && self.isSpeaking ) + { + self waittill( "done speaking" ); + wait( 0.1 ); + } + + self animscripts\battlechatter_ai::removeFromSystem(); + waittillframeend; + self.voice = newvoice; + self animscripts\battlechatter_ai::addToSystem(); +} + +/* +============= +///ScriptDocBegin +"Name: flavorbursts_on( )" +"Summary: Gives all the AIs on a team the ability to play flavor burst transmissions. (Only US allies can use FBTs.) Note: if the battlechatter system is not working, the flavorbursts will not work even if the AIs have this set on them." +"Module: Battlechatter" +"CallOn: " +"OptionalArg: : which team? Usually, only allies have flavorbursts." +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +flavorbursts_on( team ) +{ + thread set_flavorbursts_team_state( true, team ); +} + +/* +============= +///ScriptDocBegin +"Name: flavorbursts_off( )" +"Summary: Removes the ability to play flavor burst transmissions from all AIs on a team. (Only US allies can use FBTs.)" +"Module: Battlechatter" +"CallOn: " +"OptionalArg: : which team? Usually, only allies have flavorbursts." +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +flavorbursts_off( team ) +{ + thread set_flavorbursts_team_state( false, team ); +} + +set_flavorbursts_team_state( state, team ) +{ + if ( !IsDefined( team ) ) + { + team = "allies"; + } + + if ( !anim.chatInitialized ) + { + return; + } + + // buffer time + wait( 1.5 ); + + level.flavorbursts[ team ] = state; + + guys = []; + guys = GetAIArray( team ); + + array_thread( guys, ::set_flavorbursts, state ); +} + +/* +============= +///ScriptDocBegin +"Name: set_flavorbursts( )" +"Summary: Turns battlechatter flavor burst transmissions for an AI on or off" +"Module: Entity" +"CallOn: An AI" +"MandatoryArg: " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_flavorbursts( state ) +{ + self.flavorbursts = state; +} + +/* +============= +///ScriptDocBegin +"Name: friendlyfire_warnings_off()" +"Summary: Disables player-originated friendly fire warnings for all allied AI." +"Module: Entity" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +friendlyfire_warnings_off() +{ + ais = GetAiArray( "allies" ); + + foreach( guy in ais ) + { + if( IsAlive( guy ) ) + { + guy set_friendlyfire_warnings( false ); + } + } + + level.friendlyfire_warnings = false; +} + +/* +============= +///ScriptDocBegin +"Name: friendlyfire_warnings_on()" +"Summary: Enables player-originated friendly fire warnings for all allied AI." +"Module: Entity" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +friendlyfire_warnings_on() +{ + ais = GetAiArray( "allies" ); + + foreach( guy in ais ) + { + if( IsAlive( guy ) ) + { + guy set_friendlyfire_warnings( true ); + } + } + + level.friendlyfire_warnings = true; +} + +/* +============= +///ScriptDocBegin +"Name: set_friendlyfire_warnings( )" +"Summary: Turns player-originated friendly fire warnings for an AI on or off" +"Module: Entity" +"CallOn: An AI" +"MandatoryArg: " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_friendlyfire_warnings( state ) +{ + if( state ) + { + self.friendlyfire_warnings_disable = undefined; + } + else + { + self.friendlyfire_warnings_disable = true; + } +} + + +// Newvillers objective management + /* + level.currentObjective = "obj1";// disables non obj1 friendly chains if you're using newvillers style friendlychains + objEvent = get_obj_event( "center_house" );// a trigger with targetname objective_event and a script_deathchain value + + objEvent waittill_objectiveEvent();// this waits until the AI with the event's script_deathchain are dead, + then waits for trigger from the player. If it targets a friendly chain then it'll + make the friendlies go to the chain. + */ + + + +/* +"Name: dds_set_player_character_name( )" +"Summary: Sets the player character name in DDS so correct player DDS is played. Default for COD7 is Mason." +"Module: Battlechatter" +"CallOn: Player" +"MandatoryArg: : the player's hero name" +"Example: player dds_set_player_character_name( "hudson" );" +"SPMP: singleplayer" +*/ +dds_set_player_character_name( hero_name ) +{ + if( !IsPlayer( self ) ) + { + /#PrintLn( "dds 'dds_set_player_character_name' function was not called on a player. No changes made." );#/ + return; + } + + switch( hero_name ) + { + case "mason": + case "hudson": + case "reznov": + level.dds.player_character_name = GetSubStr( hero_name, 0, 3 ); + /#PrintLn( "dds setting player name to '" + level.dds.player_character_name + "'" );#/ + break; + default: + /#printLn( "dds: '" + hero_name + "' not a valid player name; setting to 'mason' (mas)" );#/ + level.dds.player_character_name = "mas"; + break; + } + self.dds_characterID = level.dds.player_character_name; +} + + +/* +"Name: dds_exclude_this_ai()" +"Summary: Mark an AI to not be in DDS and to not say any DDS lines." +"Module: Battlechatter" +"CallOn: AI" +"MandatoryArg: " +"Example: us_redshirt dds_exclude_this_ai();" +"SPMP: singleplayer" +*/ +dds_exclude_this_ai( bExclude ) +{ + if( IsAI( self ) && IsAlive( self ) ) + { + if (bExclude) + { + self.dds_disable = true; + } + else + { + self.dds_disable = false; + } + } + else + { + /#PrintLn( "Tried to mark an entity for DDS removal that was not an AI or not alive." );#/ + } +} + + +get_obj_origin( msg ) +{ + objOrigins = GetEntArray( "objective", "targetname" ); + for ( i = 0; i < objOrigins.size; i++ ) + { + if ( objOrigins[ i ].script_noteworthy == msg ) + return objOrigins[ i ].origin; + } +} + +get_obj_event( msg ) +{ + objEvents = GetEntArray( "objective_event", "targetname" ); + for ( i = 0; i < objEvents.size; i++ ) + { + if ( objEvents[ i ].script_noteworthy == msg ) + return objEvents[ i ]; + } +} + + +waittill_objective_event() +{ + waittill_objective_event_proc( true ); +} + +waittill_objective_event_notrigger() +{ + waittill_objective_event_proc( false ); +} + +debugorigin() +{ +// self endon( "killanimscript" ); + + self notify( "Debug origin" ); + self endon( "Debug origin" ); + self endon( "death" ); + for ( ;; ) + { + forward = AnglesToForward( self.angles ); + forwardFar = ( forward * 30 ); + forwardClose = ( forward * 20 ); + right = AnglesToRight( self.angles ); + left = ( right * -10 ); + right = ( right * 10 ); + Line( self.origin, self.origin + forwardFar, ( 0.9, 0.7, 0.6 ), 0.9 ); + Line( self.origin + forwardFar, self.origin + forwardClose + right, ( 0.9, 0.7, 0.6 ), 0.9 ); + Line( self.origin + forwardFar, self.origin + forwardClose + left, ( 0.9, 0.7, 0.6 ), 0.9 ); + wait( 0.05 ); + } +} + + + +/* +============= +///ScriptDocBegin +"Name: get_linked_structs()" +"Summary: Returns an array of entities that SELF is linked to" +"Module: Utility" +"CallOn: An entity that links to other entities" +"Example: spawners = heli get_linked_structs()" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +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; +} + + /* + ============= +///ScriptDocBegin +"Name: get_last_ent_in_chain( )" +"Summary: Get the last entity/node/vehiclenode in a chain of targeted entities" +"Module: Entity" +"CallOn: Any entity that targets a chain of linked nodes, vehiclenodes or other entities like script_origin" +"MandatoryArg: : needs to be specified as 'vehiclenode', 'pathnode', 'ent' or 'struct'" +"Example: eLastNode = eVehicle get_last_ent_in_chain( "vehiclenode" );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +get_last_ent_in_chain( sEntityType ) +{ + ePathpoint = self; + while ( IsDefined( ePathpoint.target ) ) + { + wait( 0.05 ); + if ( IsDefined( ePathpoint.target ) ) + { + switch( sEntityType ) + { + case "vehiclenode": + ePathpoint = GetVehicleNode( ePathpoint.target, "targetname" ); + break; + case "pathnode": + ePathpoint = GetNode( ePathpoint.target, "targetname" ); + break; + case "ent": + ePathpoint = GetEnt( ePathpoint.target, "targetname" ); + break; + case "struct": + ePathpoint = getstruct( ePathpoint.target, "targetname" ); + break; + default: + AssertMsg( "sEntityType needs to be 'vehiclenode', 'pathnode', 'ent' or 'struct'" ); + } + } + else + break; + } + ePathend = ePathpoint; + return ePathend; +} + + +player_seek( timeout ) +{ + goalent = Spawn( "script_origin", level.player.origin ); + goalent LinkTo( level.player ); + if ( IsDefined( timeout ) ) + self thread timeout( timeout ); + self SetGoalEntity( goalent ); + if ( !isdefined( self.oldgoalradius ) ) + self.oldgoalradius = self.goalradius; + self.goalradius = 300; + self waittill_any( "goal", "timeout" ); + if ( IsDefined( self.oldgoalradius ) ) + { + self.goalradius = self.oldgoalradius; + self.oldgoalradius = undefined; + } + goalent Delete(); +} + +timeout( timeout ) +{ + self endon( "death" ); + wait( timeout ); + self notify( "timeout" ); +} + +/* +============= +///ScriptDocBegin +"Name: set_forcegoal( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +set_forcegoal() +{ + if ( IsDefined( self.set_forcedgoal ) ) + return; + + self.oldfightdist = self.pathenemyfightdist; + self.oldmaxdist = self.pathenemylookahead; + self.oldmaxsight = self.maxsightdistsqrd; + + self.pathenemyfightdist = 8; + self.pathenemylookahead = 8; + self.maxsightdistsqrd = 1; + self.set_forcedgoal = true; +} + +/* +============= +///ScriptDocBegin +"Name: unset_forcegoal( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +unset_forcegoal() +{ + if ( !isdefined( self.set_forcedgoal ) ) + return; + + self.pathenemyfightdist = self.oldfightdist; + self.pathenemylookahead = self.oldmaxdist; + self.maxsightdistsqrd = self.oldmaxsight; + self.set_forcedgoal = undefined; +} + +/* +============= +///ScriptDocBegin +"Name: array_removeDead_keepkeys( )" +"Summary: Remove dead from an array but keep keys intact" +"Module: Utility" +"CallOn: An array" +"MandatoryArg: : The array " +"Example: array = array_removeDead_keepkeys( );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_removeDead_keepkeys( array ) +{ + newArray = []; + keys = GetArrayKeys( array ); + for ( i = 0; i < keys.size; i++ ) + { + key = keys[ i ]; + if ( !isalive( array[ key ] ) ) + continue; + newArray[ key ] = array[ key ]; + } + + return newArray; +} + + /* + ============= +///ScriptDocBegin +"Name: array_removeDead( )" +"Summary: Returns a new array of < array > minus the dead entities" +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to search for dead entities in." +"Example: friendlies = array_removeDead( friendlies );" +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ +array_removeDead( array ) +{ + newArray = []; + foreach ( member in array ) + { + if ( !isalive( member ) ) + continue; + newArray[ newArray.size ] = member; + } + + return newArray; +} + +/* +============= +///ScriptDocBegin +"Name: array_removeDead_or_dying( )" +"Summary: Returns a new array of < array > minus the dead or dying ai" +"Module: Array" +"CallOn: " +"MandatoryArg: : The array to search for dead ai in." +"Example: friendlies = array_removeDead_or_dying( friendlies );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_removeDead_or_dying( array ) +{ + newArray = []; + foreach ( member in array ) + { + if ( !isalive( member ) ) + continue; + if ( member doingLongDeath() ) + continue; + newArray[ newArray.size ] = member; + } + + return newArray; + +} + + +/* +============= +///ScriptDocBegin +"Name: array_remove_nokeys( , )" +"Summary: array_remove used on non keyed arrays doesn't flip the array " +"Module: Utility" +"CallOn: Level" +"MandatoryArg: : array to remove from" +"MandatoryArg: : thing to remove from the array" +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ + +array_remove_nokeys( ents, remover ) +{ + newents = []; + for ( i = 0; i < ents.size; i++ ) + if ( ents[ i ] != remover ) + newents[ newents.size ] = ents[ i ]; + return newents; +} + +/* +============= +///ScriptDocBegin +"Name: array_remove_index( , )" +"Summary: Removes the element in the array with this index, resulting array order is intact." +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_remove_index( array, index ) +{ + for ( i = 0; i < array.size - 1; i++ ) + { + if ( i == index ) + { + array[ i ] = array[ i + 1 ]; + index++; + } + } + array[ array.size - 1 ] = undefined; + return array; +} + +/* +============= +///ScriptDocBegin +"Name: array_notify( , )" +"Summary: notify this array of entities with " +"Module: Array" +"CallOn: array of entities" +"MandatoryArg: : " +"MandatoryArg: : " +"Example: array_notify( enemies, "time_to_dance" )" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +array_notify( ents, notifier, match ) +{ + foreach ( key, value in ents ) + value notify( notifier, match ); +} + + +// fancy quicker struct array handling, assumes array elements are objects with which an index can be asigned to( IE: can't do 5.struct_array_index ) +// also have to be sure that objects can't be a part of another structarray setup as the index position is asigned to the object + + +struct_arrayspawn() +{ + struct = SpawnStruct(); + struct.array = []; + struct.lastindex = 0; + return struct; +} + + /* +structarray_add( struct, object ) +{ + struct.array[ struct.lastindex ] = SpawnStruct(); + struct.array[ struct.lastindex ].object = object; + struct.array[ struct.lastindex ].struct_array_index = struct.lastindex; + struct.lastindex ++ ; +} + */ +structarray_add( struct, object ) +{ + Assert( !isdefined( object.struct_array_index ) );// can't have elements of two structarrays on these. can add that later if it's needed + struct.array[ struct.lastindex ] = object; + object.struct_array_index = struct.lastindex; + struct.lastindex++; +} + + /* + ============= +///ScriptDocBegin +"Name: structarray_remove( , : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd + ============= + */ + +structarray_remove( struct, object ) +{ + structarray_swaptolast( struct, object ); + struct.array[ struct.lastindex - 1 ] = undefined; + struct.lastindex--; +} + +structarray_remove_index( struct, index ) +{ + // if it has a last item, copy it to the index and delete the last + if ( IsDefined( struct.array[ struct.lastindex - 1 ] ) ) + { + // overwrite the index to remove with the last in the array + struct.array[ index ] = struct.array[ struct.lastindex - 1 ]; + struct.array[ index ].struct_array_index = index; + + // remove the last from the array since it's now at position 'index' + struct.array[ struct.lastindex - 1 ] = undefined; + struct.lastindex = struct.array.size; + } + else + { + // is not guaranteed to have a last item, could have been + // deleted the same frame, rebuild the array + struct.array[ index ] = undefined; + structarray_remove_undefined( struct ); + } +} + +structarray_remove_undefined( struct ) +{ + // remove undefined ents from array + newArray = []; + foreach( object in struct.array ) + { + if ( !isdefined( object ) ) + continue; + newArray[ newArray.size ] = object; + } + struct.array = newArray; + + // reassign all the index vars + foreach( i, object in struct.array ) + { + object.struct_array_index = i; + } + struct.lastindex = struct.array.size; +} + +structarray_swaptolast( struct, object ) +{ + struct structarray_swap( struct.array[ struct.lastindex - 1 ], object ); +} + +structarray_shuffle( struct, shuffle ) +{ + for ( i = 0; i < shuffle; i++ ) + struct structarray_swap( struct.array[ i ], struct.array[ RandomInt( struct.lastindex ) ] ); +} + +/* +============= +///ScriptDocBegin +"Name: get_use_key( )" +"Summary: " +"Module: Entity" +"CallOn: An entity" +"MandatoryArg: : " +"OptionalArg: : " +"Example: " +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +get_use_key() +{ + if ( level.console ) + return " + usereload"; + else + return " + activate"; +} + +/* +============= +///ScriptDocBegin +"Name: custom_battlechatter( )" +"Summary: Call this on an AI to get him to use the battlechatter system to say a specific kind of phrase. AIs who have battlechatter turned off won't be able to say the phrase. Returns false if it couldn't do the custom battlechatter for some reason (will output to console or assert with errors, depending on severity)." +"Module: Battlechatter" +"CallOn: An AI" +"MandatoryArg: : the string ID for the phrase that the AI will try to say. Legit phrases are: "order_move_combat", "order_move_noncombat", "order_action_coverme", "inform_reloading"" +"Example: level.sarge custom_battlechatter( "move_combat" );" +"SPMP: singleplayer" +///ScriptDocEnd +============= +*/ +custom_battlechatter( phrase ) +{ + return self animscripts\battlechatter_ai::custom_battlechatter_internal( phrase ); +} + +/* +============= +///ScriptDocBegin +"Name: get_stop_watch(